Allow selection of input and output devices in EngineDialog for backends that support it
[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/mididm.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/types.h"
46 #include "ardour/profile.h"
47
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
50
51 #include "opts.h"
52 #include "ardour_ui.h"
53 #include "engine_dialog.h"
54 #include "gui_thread.h"
55 #include "utils.h"
56 #include "i18n.h"
57
58 using namespace std;
59 using namespace Gtk;
60 using namespace Gtkmm2ext;
61 using namespace PBD;
62 using namespace Glib;
63 using namespace ARDOUR_UI_UTILS;
64
65 static const unsigned int midi_tab = 2;
66 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
67
68 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
69
70 EngineControl::EngineControl ()
71         : ArdourDialog (_("Audio/MIDI Setup"))
72         , engine_status ("")
73         , basic_packer (9, 4)
74         , input_latency_adjustment (0, 0, 99999, 1)
75         , input_latency (input_latency_adjustment)
76         , output_latency_adjustment (0, 0, 99999, 1)
77         , output_latency (output_latency_adjustment)
78         , input_channels_adjustment (0, 0, 256, 1)
79         , input_channels (input_channels_adjustment)
80         , output_channels_adjustment (0, 0, 256, 1)
81         , output_channels (output_channels_adjustment)
82         , ports_adjustment (128, 8, 1024, 1, 16)
83         , ports_spinner (ports_adjustment)
84         , control_app_button (_("Device Control Panel"))
85         , midi_devices_button (_("Midi Device Setup"))
86         , lm_measure_label (_("Measure"))
87         , lm_use_button (_("Use results"))
88         , lm_back_button (_("Back to settings ... (ignore results)"))
89         , lm_button_audio (_("Calibrate Audio"))
90         , lm_table (12, 3)
91         , have_lm_results (false)
92         , lm_running (false)
93         , midi_back_button (_("Back to settings"))
94         , ignore_changes (0)
95         , _desired_sample_rate (0)
96         , started_at_least_once (false)
97         , queue_device_changed (false)
98 {
99         using namespace Notebook_Helpers;
100         vector<string> backend_names;
101         Label* label;
102         AttachOptions xopt = AttachOptions (FILL|EXPAND);
103         int row;
104
105         set_name (X_("AudioMIDISetup"));
106
107         /* the backend combo is the one thing that is ALWAYS visible */
108
109         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
110
111         if (backends.empty()) {
112                 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));
113                 msg.run ();
114                 throw failed_constructor ();
115         }
116
117         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
118                 backend_names.push_back ((*b)->name);
119         }
120
121         set_popdown_strings (backend_combo, backend_names);
122         backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
123
124         /* setup basic packing characteristics for the table used on the main
125          * tab of the notebook
126          */
127
128         basic_packer.set_spacings (6);
129         basic_packer.set_border_width (12);
130         basic_packer.set_homogeneous (false);
131
132         /* pack it in */
133
134         basic_hbox.pack_start (basic_packer, false, false);
135
136         /* latency measurement tab */
137
138         lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
139
140         row = 0;
141         lm_table.set_row_spacings (12);
142         lm_table.set_col_spacings (6);
143         lm_table.set_homogeneous (false);
144
145         lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
146         row++;
147
148         lm_preamble.set_width_chars (60);
149         lm_preamble.set_line_wrap (true);
150         lm_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 (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
153         row++;
154
155         Gtk::Label* preamble;
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_label.set_padding (10, 10);
183         lm_measure_button.add (lm_measure_label);
184         lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
185         lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
186         lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
187
188         lm_use_button.set_sensitive (false);
189
190         /* Increase the default spacing around the labels of these three
191          * buttons
192          */
193
194         Gtk::Misc* l;
195
196         if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
197                 l->set_padding (10, 10);
198         }
199
200         if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
201                 l->set_padding (10, 10);
202         }
203
204         preamble = manage (new Label);
205         preamble->set_width_chars (60);
206         preamble->set_line_wrap (true);
207         preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
208         lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
209         row++;
210
211         preamble = manage (new Label);
212         preamble->set_width_chars (60);
213         preamble->set_line_wrap (true);
214         preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
215         lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
216
217         ++row; // skip a row in the table
218         ++row; // skip a row in the table
219
220         lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
221
222         ++row; // skip a row in the table
223         ++row; // skip a row in the table
224
225         lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
226         lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
227         lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
228
229         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
230
231         lm_vbox.set_border_width (12);
232         lm_vbox.pack_start (lm_table, false, false);
233
234         midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
235
236         /* pack it all up */
237
238         notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
239         notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
240         notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
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         get_action_area()->pack_start (engine_status);
254         engine_status.show();
255
256         /* need a special function to print "all available channels" when the
257          * channel counts hit zero.
258          */
259
260         input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
261         output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
262
263         midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
264         midi_devices_button.set_sensitive (false);
265         midi_devices_button.set_name ("generic button");
266         midi_devices_button.set_can_focus(true);
267
268         control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
269         manage_control_app_sensitivity ();
270
271         cancel_button = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL);
272         apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
273         ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
274
275         /* Pick up any existing audio setup configuration, if appropriate */
276
277         XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
278
279         ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
280         ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
281         ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
282         ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
283
284         if (audio_setup) {
285                 set_state (*audio_setup);
286         }
287
288         if (backend_combo.get_active_text().empty()) {
289                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
290                 backend_combo.set_active_text (backend_names.front());
291         }
292
293         backend_changed ();
294
295         /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
296         if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
297                 backend_combo.set_active_text (backend_names.back());
298                 /* ignore: don't save state */
299                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
300                 backend_changed ();
301         }
302
303
304         /* Connect to signals */
305
306         driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
307         sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
308         buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
309         device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
310         midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
311
312         input_device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::input_device_changed));
313         output_device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::output_device_changed));
314
315         input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
316         output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
317         input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
318         output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
319
320         notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
321
322         connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
323         connect_disconnect_button.set_no_show_all();
324
325 }
326
327 void
328 EngineControl::on_show ()
329 {
330         ArdourDialog::on_show ();
331         if (!ARDOUR::AudioEngine::instance()->current_backend() || !ARDOUR::AudioEngine::instance()->running()) {
332                 // re-check _have_control (jackd running) see #6041
333                 backend_changed ();
334         }
335         device_changed ();
336         input_device_changed ();
337         output_device_changed ();
338         ok_button->grab_focus();
339 }
340
341 void
342 EngineControl::on_response (int response_id)
343 {
344         ArdourDialog::on_response (response_id);
345
346         switch (response_id) {
347                 case RESPONSE_APPLY:
348                         push_state_to_backend (true);
349                         break;
350                 case RESPONSE_OK:
351 #ifdef PLATFORM_WINDOWS
352                         // For some reason we don't understand, 'hide()'
353                         // needs to get called first in Windows
354                         hide ();
355
356                         // But if there's no session open, this can produce
357                         // a long gap when nothing appears to be happening.
358                         // Let's show the splash image while we're waiting.
359                         if ( !ARDOUR_COMMAND_LINE::no_splash ) {
360                                 if ( ARDOUR_UI::instance() ) {
361                                         if ( !ARDOUR_UI::instance()->session_loaded ) {
362                                                 ARDOUR_UI::instance()->show_splash();
363                                         }
364                                 }
365                         }
366                         push_state_to_backend (true);
367                         break;
368 #else
369                         push_state_to_backend (true);
370                         hide ();
371                         break;
372 #endif
373                 case RESPONSE_DELETE_EVENT:
374                         {
375                                 GdkEventButton ev;
376                                 ev.type = GDK_BUTTON_PRESS;
377                                 ev.button = 1;
378                                 on_delete_event ((GdkEventAny*) &ev);
379                                 break;
380                         }
381                 default:
382                         hide ();
383         }
384 }
385
386 void
387 EngineControl::build_notebook ()
388 {
389         Label* label;
390         AttachOptions xopt = AttachOptions (FILL|EXPAND);
391
392         /* clear the table */
393
394         Gtkmm2ext::container_clear (basic_vbox);
395         Gtkmm2ext::container_clear (basic_packer);
396
397         if (control_app_button.get_parent()) {
398                 control_app_button.get_parent()->remove (control_app_button);
399         }
400
401         label = manage (left_aligned_label (_("Audio System:")));
402         basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
403         basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
404
405         lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
406         lm_button_audio.set_name ("generic button");
407         lm_button_audio.set_can_focus(true);
408
409         if (_have_control) {
410                 build_full_control_notebook ();
411         } else {
412                 build_no_control_notebook ();
413         }
414
415         basic_vbox.pack_start (basic_hbox, false, false);
416
417         {
418                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
419                 basic_vbox.show_all ();
420         }
421 }
422
423 void
424 EngineControl::build_full_control_notebook ()
425 {
426         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
427         assert (backend);
428
429         using namespace Notebook_Helpers;
430         Label* label;
431         vector<string> strings;
432         AttachOptions xopt = AttachOptions (FILL|EXPAND);
433         int row = 1; // row zero == backend combo
434
435         /* start packing it up */
436
437         if (backend->requires_driver_selection()) {
438                 label = manage (left_aligned_label (_("Driver:")));
439                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
440                 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
441                 row++;
442         }
443
444         if (backend->use_separate_input_and_output_devices()) {
445                 label = manage (left_aligned_label (_("Input Device:")));
446                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
447                 basic_packer.attach (input_device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
448                 row++;
449                 label = manage (left_aligned_label (_("Output Device:")));
450                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
451                 basic_packer.attach (output_device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
452                 row++;
453                 // reset so it isn't used in state comparisons
454                 device_combo.set_active_text ("");
455         } else {
456                 label = manage (left_aligned_label (_("Device:")));
457                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
458                 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
459                 row++;
460                 // reset these so they don't get used in state comparisons
461                 input_device_combo.set_active_text ("");
462                 output_device_combo.set_active_text ("");
463         }
464
465         label = manage (left_aligned_label (_("Sample rate:")));
466         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
467         basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
468         row++;
469
470
471         label = manage (left_aligned_label (_("Buffer size:")));
472         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
473         basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
474         buffer_size_duration_label.set_alignment (0.0); /* left-align */
475         basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
476
477         /* button spans 2 rows */
478
479         basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
480         row++;
481
482         input_channels.set_name ("InputChannels");
483         input_channels.set_flags (Gtk::CAN_FOCUS);
484         input_channels.set_digits (0);
485         input_channels.set_wrap (false);
486         output_channels.set_editable (true);
487
488         if (!ARDOUR::Profile->get_mixbus()) {
489                 label = manage (left_aligned_label (_("Input Channels:")));
490                 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
491                 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
492                 ++row;
493         }
494         
495         output_channels.set_name ("OutputChannels");
496         output_channels.set_flags (Gtk::CAN_FOCUS);
497         output_channels.set_digits (0);
498         output_channels.set_wrap (false);
499         output_channels.set_editable (true);
500
501         if (!ARDOUR::Profile->get_mixbus()) {
502                 label = manage (left_aligned_label (_("Output Channels:")));
503                 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
504                 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
505                 ++row;
506         }
507         
508         input_latency.set_name ("InputLatency");
509         input_latency.set_flags (Gtk::CAN_FOCUS);
510         input_latency.set_digits (0);
511         input_latency.set_wrap (false);
512         input_latency.set_editable (true);
513
514         label = manage (left_aligned_label (_("Hardware input latency:")));
515         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
516         basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
517         label = manage (left_aligned_label (_("samples")));
518         basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
519         ++row;
520
521         output_latency.set_name ("OutputLatency");
522         output_latency.set_flags (Gtk::CAN_FOCUS);
523         output_latency.set_digits (0);
524         output_latency.set_wrap (false);
525         output_latency.set_editable (true);
526
527         label = manage (left_aligned_label (_("Hardware output latency:")));
528         basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
529         basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
530         label = manage (left_aligned_label (_("samples")));
531         basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
532
533         /* button spans 2 rows */
534
535         basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
536         ++row;
537
538         label = manage (left_aligned_label (_("MIDI System:")));
539         basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
540         basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
541         basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
542         row++;
543 }
544
545 void
546 EngineControl::build_no_control_notebook ()
547 {
548         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
549         assert (backend);
550
551         using namespace Notebook_Helpers;
552         Label* label;
553         vector<string> strings;
554         AttachOptions xopt = AttachOptions (FILL|EXPAND);
555         int row = 1; // row zero == backend combo
556         const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
557
558         label = manage (new Label);
559         label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
560         basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
561         row++;
562
563         if (backend->can_change_sample_rate_when_running()) {
564                 label = manage (left_aligned_label (_("Sample rate:")));
565                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
566                 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
567                 row++;
568         }
569
570         if (backend->can_change_buffer_size_when_running()) {
571                 label = manage (left_aligned_label (_("Buffer size:")));
572                 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
573                 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
574                 buffer_size_duration_label.set_alignment (0.0); /* left-align */
575                 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
576                 row++;
577         }
578
579         basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
580         row++;
581 }
582
583 EngineControl::~EngineControl ()
584 {
585         ignore_changes = true;
586 }
587
588 void
589 EngineControl::disable_latency_tab ()
590 {
591         vector<string> empty;
592         set_popdown_strings (lm_output_channel_combo, empty);
593         set_popdown_strings (lm_input_channel_combo, empty);
594         lm_measure_button.set_sensitive (false);
595         lm_use_button.set_sensitive (false);
596 }
597
598 void
599 EngineControl::enable_latency_tab ()
600 {
601         vector<string> outputs;
602         vector<string> inputs;
603
604         ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
605         ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
606         ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
607
608         if (!ARDOUR::AudioEngine::instance()->running()) {
609                 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
610                 notebook.set_current_page (0);
611                 msg.run ();
612                 return;
613         }
614         else if (inputs.empty() || outputs.empty()) {
615                 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
616                 notebook.set_current_page (0);
617                 msg.run ();
618                 return;
619         }
620
621         lm_back_button_signal.disconnect();
622         if (_measure_midi) {
623                 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
624                 lm_preamble.hide ();
625         } else {
626                 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
627                 lm_preamble.show ();
628         }
629
630         set_popdown_strings (lm_output_channel_combo, outputs);
631         lm_output_channel_combo.set_active_text (outputs.front());
632         lm_output_channel_combo.set_sensitive (true);
633
634         set_popdown_strings (lm_input_channel_combo, inputs);
635         lm_input_channel_combo.set_active_text (inputs.front());
636         lm_input_channel_combo.set_sensitive (true);
637
638         lm_measure_button.set_sensitive (true);
639 }
640
641 void
642 EngineControl::setup_midi_tab_for_backend ()
643 {
644         string backend = backend_combo.get_active_text ();
645
646         Gtkmm2ext::container_clear (midi_vbox);
647
648         midi_vbox.set_border_width (12);
649         midi_device_table.set_border_width (12);
650
651         if (backend == "JACK") {
652                 setup_midi_tab_for_jack ();
653         }
654
655         midi_vbox.pack_start (midi_device_table, true, true);
656         midi_vbox.pack_start (midi_back_button, false, false);
657         midi_vbox.show_all ();
658 }
659
660 void
661 EngineControl::setup_midi_tab_for_jack ()
662 {
663 }
664
665 void
666 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
667         if (for_input) {
668                 device->input_latency = a->get_value();
669         } else {
670                 device->output_latency = a->get_value();
671         }
672 }
673
674 void
675 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
676         b->set_active (!b->get_active());
677         device->enabled = b->get_active();
678         refresh_midi_display(device->name);
679 }
680
681 void
682 EngineControl::refresh_midi_display (std::string focus)
683 {
684         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
685         assert (backend);
686
687         int row  = 0;
688         AttachOptions xopt = AttachOptions (FILL|EXPAND);
689         Gtk::Label* l;
690
691         Gtkmm2ext::container_clear (midi_device_table);
692
693         midi_device_table.set_spacings (6);
694
695         l = manage (new Label);
696         l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
697         midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
698         l->set_alignment (0.5, 0.5);
699         row++;
700         l->show ();
701
702         l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
703         midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
704         l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
705         midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
706         row++;
707         l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
708         midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
709         l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
710         midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
711         row++;
712
713         for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
714                 ArdourButton *m;
715                 Gtk::Button* b;
716                 Gtk::Adjustment *a;
717                 Gtk::SpinButton *s;
718                 bool enabled = (*p)->enabled;
719
720                 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
721                 m->set_name ("midi device");
722                 m->set_can_focus (Gtk::CAN_FOCUS);
723                 m->add_events (Gdk::BUTTON_RELEASE_MASK);
724                 m->set_active (enabled);
725                 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
726                 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
727                 if ((*p)->name == focus) {
728                         m->grab_focus();
729                 }
730
731                 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
732                 s = manage (new Gtk::SpinButton (*a));
733                 a->set_value ((*p)->input_latency);
734                 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
735                 s->set_sensitive (_can_set_midi_latencies && enabled);
736                 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
737
738                 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
739                 s = manage (new Gtk::SpinButton (*a));
740                 a->set_value ((*p)->output_latency);
741                 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
742                 s->set_sensitive (_can_set_midi_latencies && enabled);
743                 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
744
745                 b = manage (new Button (_("Calibrate")));
746                 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
747                 b->set_sensitive (_can_set_midi_latencies && enabled);
748                 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
749
750                 row++;
751         }
752 }
753
754 void
755 EngineControl::update_sensitivity ()
756 {
757 }
758
759 void
760 EngineControl::backend_changed ()
761 {
762         string backend_name = backend_combo.get_active_text();
763         boost::shared_ptr<ARDOUR::AudioBackend> backend;
764
765         if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
766                 /* eh? setting the backend failed... how ? */
767                 /* A: stale config contains a backend that does not exist in current build */
768                 return;
769         }
770
771         _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
772
773         build_notebook ();
774         setup_midi_tab_for_backend ();
775         _midi_devices.clear();
776
777         if (backend->requires_driver_selection()) {
778                 vector<string> drivers = backend->enumerate_drivers();
779                 driver_combo.set_sensitive (true);
780
781                 if (!drivers.empty()) {
782                         {
783                                 string current_driver;
784                                 current_driver = backend->driver_name ();
785
786                                 // driver might not have been set yet
787                                 if (current_driver == "") {
788                                         current_driver = driver_combo.get_active_text ();
789                                         if (current_driver == "")
790                                                 // driver has never been set, make sure it's not blank
791                                                 current_driver = drivers.front ();
792                                 }
793
794                                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
795                                 set_popdown_strings (driver_combo, drivers);
796                                 driver_combo.set_active_text (current_driver);
797                         }
798
799                         driver_changed ();
800                 }
801
802         } else {
803                 driver_combo.set_sensitive (false);
804                 /* this will change the device text which will cause a call to
805                  * device changed which will set up parameters
806                  */
807                 list_devices ();
808         }
809
810         vector<string> midi_options = backend->enumerate_midi_options();
811
812         if (midi_options.size() == 1) {
813                 /* only contains the "none" option */
814                 midi_option_combo.set_sensitive (false);
815         } else {
816                 if (_have_control) {
817                         set_popdown_strings (midi_option_combo, midi_options);
818                         midi_option_combo.set_active_text (midi_options.front());
819                         midi_option_combo.set_sensitive (true);
820                 } else {
821                         midi_option_combo.set_sensitive (false);
822                 }
823         }
824
825         connect_disconnect_button.hide();
826
827         midi_option_changed();
828
829         started_at_least_once = false;
830
831         if (!ignore_changes) {
832                 maybe_display_saved_state ();
833         }
834 }
835
836 bool
837 EngineControl::print_channel_count (Gtk::SpinButton* sb)
838 {
839         if (ARDOUR::Profile->get_mixbus()) {
840                 return true;
841         }
842         
843         uint32_t cnt = (uint32_t) sb->get_value();
844         if (cnt == 0) {
845                 sb->set_text (_("all available channels"));
846         } else {
847                 char buf[32];
848                 snprintf (buf, sizeof (buf), "%d", cnt);
849                 sb->set_text (buf);
850         }
851         return true;
852 }
853
854 // @return true if there are devices available
855 bool
856 EngineControl::set_device_popdown_strings ()
857 {
858         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
859         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
860
861         /* NOTE: Ardour currently does not display the "available" field of the
862          * returned devices.
863          *
864          * Doing so would require a different GUI widget than the combo
865          * box/popdown that we currently use, since it has no way to list
866          * items that are not selectable. Something more like a popup menu,
867          * which could have unselectable items, would be appropriate.
868          */
869
870         vector<string> available_devices;
871
872         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
873                 available_devices.push_back (i->name);
874         }
875
876
877         if (!available_devices.empty()) {
878                 update_sensitivity ();
879
880                 {
881                         string current_device, found_device;
882                         current_device = device_combo.get_active_text ();
883                         if (current_device == "") {
884                                 current_device = backend->device_name ();
885                         }
886
887                         // Make sure that the active text is still relevant for this
888                         // device (it might only be relevant to the previous device!!)
889                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
890                                 if (*i == current_device)
891                                         found_device = current_device;
892                         }
893                         if (found_device == "")
894                                 // device has never been set (or was not relevant
895                                 // for this backend) Let's make sure it's not blank
896                                 current_device = available_devices.front ();
897
898                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
899                         set_popdown_strings (device_combo, available_devices);
900
901                         device_combo.set_active_text (current_device);
902                 }
903
904                 device_changed ();
905                 return true;
906         }
907         return false;
908 }
909
910 // @return true if there are input devices available
911 bool
912 EngineControl::set_input_device_popdown_strings ()
913 {
914         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
915         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_input_devices ();
916
917         vector<string> available_devices;
918
919         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
920                 available_devices.push_back (i->name);
921         }
922
923         if (!available_devices.empty()) {
924                 update_sensitivity ();
925
926                 {
927                         string current_device, found_device;
928                         current_device = input_device_combo.get_active_text ();
929                         if (current_device == "") {
930                                 current_device = backend->input_device_name ();
931                         }
932
933                         // Make sure that the active text is still relevant for this
934                         // device (it might only be relevant to the previous device!!)
935                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
936                                 if (*i == current_device)
937                                         found_device = current_device;
938                         }
939                         if (found_device == "")
940                                 // device has never been set (or was not relevant
941                                 // for this backend) Let's make sure it's not blank
942                                 current_device = available_devices.front ();
943
944                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
945                         set_popdown_strings (input_device_combo, available_devices);
946
947                         input_device_combo.set_active_text (current_device);
948                 }
949
950                 input_device_changed ();
951                 return true;
952         }
953
954         return false;
955 }
956
957 // @return true if there are output devices available
958 bool
959 EngineControl::set_output_device_popdown_strings ()
960 {
961         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
962         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_output_devices ();
963
964         vector<string> available_devices;
965
966         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
967                 available_devices.push_back (i->name);
968         }
969
970         if (!available_devices.empty()) {
971                 update_sensitivity ();
972
973                 {
974                         string current_device, found_device;
975                         current_device = output_device_combo.get_active_text ();
976                         if (current_device == "") {
977                                 current_device = backend->output_device_name ();
978                         }
979
980                         // Make sure that the active text is still relevant for this
981                         // device (it might only be relevant to the previous device!!)
982                         for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
983                                 if (*i == current_device)
984                                         found_device = current_device;
985                         }
986                         if (found_device == "")
987                                 // device has never been set (or was not relevant
988                                 // for this backend) Let's make sure it's not blank
989                                 current_device = available_devices.front ();
990
991                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
992                         set_popdown_strings (output_device_combo, available_devices);
993
994                         output_device_combo.set_active_text (current_device);
995                 }
996
997                 output_device_changed ();
998                 return true;
999         }
1000
1001         return false;
1002 }
1003
1004 void
1005 EngineControl::list_devices ()
1006 {
1007         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1008         assert (backend);
1009
1010         /* now fill out devices, mark sample rates, buffer sizes insensitive */
1011
1012         bool devices_available = false;
1013
1014         if (backend->use_separate_input_and_output_devices ()) {
1015                 bool input_devices_available = set_input_device_popdown_strings ();
1016                 bool output_devices_available = set_output_device_popdown_strings ();
1017                 devices_available = input_devices_available || output_devices_available;
1018         } else {
1019                 devices_available = set_device_popdown_strings ();
1020         }
1021
1022         if (devices_available) {
1023                 input_latency.set_sensitive (true);
1024                 output_latency.set_sensitive (true);
1025                 input_channels.set_sensitive (true);
1026                 output_channels.set_sensitive (true);
1027
1028                 ok_button->set_sensitive (true);
1029                 apply_button->set_sensitive (true);
1030
1031         } else {
1032                 device_combo.clear();
1033                 input_device_combo.clear();
1034                 output_device_combo.clear();
1035                 sample_rate_combo.set_sensitive (false);
1036                 buffer_size_combo.set_sensitive (false);
1037                 input_latency.set_sensitive (false);
1038                 output_latency.set_sensitive (false);
1039                 input_channels.set_sensitive (false);
1040                 output_channels.set_sensitive (false);
1041                 if (_have_control) {
1042                         ok_button->set_sensitive (false);
1043                         apply_button->set_sensitive (false);
1044                 } else {
1045                         ok_button->set_sensitive (true);
1046                         apply_button->set_sensitive (true);
1047                         if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
1048                                 sample_rate_combo.set_sensitive (true);
1049                         }
1050                         if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
1051                                 buffer_size_combo.set_sensitive (true);
1052                         }
1053
1054                 }
1055         }
1056 }
1057
1058 void
1059 EngineControl::driver_changed ()
1060 {
1061         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1062         assert (backend);
1063
1064         backend->set_driver (driver_combo.get_active_text());
1065         list_devices ();
1066
1067         if (!ignore_changes) {
1068                 maybe_display_saved_state ();
1069         }
1070 }
1071
1072 void
1073 EngineControl::set_samplerate_popdown_strings (const std::string& device_name)
1074 {
1075         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1076         string desired;
1077         vector<float> sr;
1078         vector<string> s;
1079
1080         if (_have_control) {
1081                 sr = backend->available_sample_rates (device_name);
1082         } else {
1083
1084                 sr.push_back (8000.0f);
1085                 sr.push_back (16000.0f);
1086                 sr.push_back (32000.0f);
1087                 sr.push_back (44100.0f);
1088                 sr.push_back (48000.0f);
1089                 sr.push_back (88200.0f);
1090                 sr.push_back (96000.0f);
1091                 sr.push_back (192000.0f);
1092                 sr.push_back (384000.0f);
1093         }
1094
1095         for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
1096                 s.push_back (rate_as_string (*x));
1097                 if (*x == _desired_sample_rate) {
1098                         desired = s.back();
1099                 }
1100         }
1101
1102         if (!s.empty()) {
1103                 sample_rate_combo.set_sensitive (true);
1104                 set_popdown_strings (sample_rate_combo, s);
1105
1106                 if (desired.empty()) {
1107                         sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
1108                 } else {
1109                         sample_rate_combo.set_active_text (desired);
1110                 }
1111
1112         } else {
1113                 sample_rate_combo.set_sensitive (false);
1114         }
1115 }
1116
1117 void
1118 EngineControl::set_buffersize_popdown_strings (const std::string& device_name)
1119 {
1120         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1121         vector<uint32_t> bs;
1122         vector<string> s;
1123
1124         if (_have_control) {
1125                 bs = backend->available_buffer_sizes (device_name);
1126         } else if (backend->can_change_buffer_size_when_running()) {
1127                 bs.push_back (8);
1128                 bs.push_back (16);
1129                 bs.push_back (32);
1130                 bs.push_back (64);
1131                 bs.push_back (128);
1132                 bs.push_back (256);
1133                 bs.push_back (512);
1134                 bs.push_back (1024);
1135                 bs.push_back (2048);
1136                 bs.push_back (4096);
1137                 bs.push_back (8192);
1138         }
1139         s.clear ();
1140         for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1141                 s.push_back (bufsize_as_string (*x));
1142         }
1143
1144         if (!s.empty()) {
1145                 buffer_size_combo.set_sensitive (true);
1146                 set_popdown_strings (buffer_size_combo, s);
1147
1148                 uint32_t period = backend->buffer_size();
1149                 if (0 == period) {
1150                         period = backend->default_buffer_size(device_name);
1151                 }
1152                 set_active_text_if_present (buffer_size_combo, bufsize_as_string (period));
1153                 show_buffer_duration ();
1154         } else {
1155                 buffer_size_combo.set_sensitive (false);
1156         }
1157 }
1158
1159 void
1160 EngineControl::device_changed ()
1161 {
1162         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1163         assert (backend);
1164         string device_name = device_combo.get_active_text ();
1165
1166         if (device_name != backend->device_name()) {
1167                 /* we set the backend-device to query various device related intormation.
1168                  * This has the side effect that backend->device_name() will match
1169                  * the device_name and  'change_device' will never be true.
1170                  * so work around this by setting...
1171                  */
1172                 queue_device_changed = true;
1173         }
1174         
1175         //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
1176         backend->set_device_name(device_name);
1177
1178         {
1179                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1180
1181                 /* don't allow programmatic change to combos to cause a
1182                    recursive call to this method.
1183                  */
1184
1185                 set_samplerate_popdown_strings (device_name);
1186                 set_buffersize_popdown_strings (device_name);
1187                 /* XXX theoretically need to set min + max channel counts here
1188                 */
1189
1190                 manage_control_app_sensitivity ();
1191         }
1192
1193         /* pick up any saved state for this device */
1194
1195         if (!ignore_changes) {
1196                 maybe_display_saved_state ();
1197         }
1198 }
1199
1200 void
1201 EngineControl::input_device_changed ()
1202 {
1203         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1204         assert (backend);
1205         string input_device_name = input_device_combo.get_active_text ();
1206
1207         if (input_device_name != backend->input_device_name()) {
1208                 queue_device_changed = true;
1209         }
1210
1211         backend->set_input_device_name(input_device_name);
1212
1213         {
1214                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1215
1216                 set_samplerate_popdown_strings (input_device_name);
1217                 set_buffersize_popdown_strings (input_device_name);
1218                 /* XXX theoretically need to set min + max channel counts here
1219                 */
1220
1221                 manage_control_app_sensitivity ();
1222         }
1223
1224         /* pick up any saved state for this device */
1225
1226         if (!ignore_changes) {
1227                 maybe_display_saved_state ();
1228         }
1229 }
1230
1231 void
1232 EngineControl::output_device_changed ()
1233 {
1234         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1235         assert (backend);
1236         string output_device_name = output_device_combo.get_active_text ();
1237
1238         if (output_device_name != backend->output_device_name()) {
1239                 queue_device_changed = true;
1240         }
1241
1242         backend->set_output_device_name(output_device_name);
1243
1244         {
1245                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1246
1247                 set_samplerate_popdown_strings (output_device_name);
1248                 set_buffersize_popdown_strings (output_device_name);
1249                 /* XXX theoretically need to set min + max channel counts here
1250                 */
1251
1252                 manage_control_app_sensitivity ();
1253         }
1254
1255         /* pick up any saved state for this device */
1256
1257         if (!ignore_changes) {
1258                 maybe_display_saved_state ();
1259         }
1260 }
1261
1262 string
1263 EngineControl::bufsize_as_string (uint32_t sz)
1264 {
1265         /* Translators: "samples" is always plural here, so no
1266            need for plural+singular forms.
1267          */
1268         char buf[64];
1269         snprintf (buf, sizeof (buf), "%u %s", sz, P_("sample", "samples", sz));
1270         return buf;
1271 }
1272
1273 void
1274 EngineControl::sample_rate_changed ()
1275 {
1276         /* reset the strings for buffer size to show the correct msec value
1277            (reflecting the new sample rate).
1278          */
1279
1280         show_buffer_duration ();
1281
1282 }
1283
1284 void
1285 EngineControl::buffer_size_changed ()
1286 {
1287         show_buffer_duration ();
1288 }
1289
1290 void
1291 EngineControl::show_buffer_duration ()
1292 {
1293
1294         /* buffer sizes  - convert from just samples to samples + msecs for
1295          * the displayed string
1296          */
1297
1298         string bs_text = buffer_size_combo.get_active_text ();
1299         uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1300         uint32_t rate = get_rate();
1301
1302         /* Developers: note the hard-coding of a double buffered model
1303            in the (2 * samples) computation of latency. we always start
1304            the audiobackend in this configuration.
1305          */
1306         /* note to jack1 developers: ardour also always starts the engine
1307          * in async mode (no jack2 --sync option) which adds an extra cycle
1308          * of latency with jack2 (and *3 would be correct)
1309          * The value can also be wrong if jackd is started externally..
1310          *
1311          * At the time of writing the ALSA backend always uses double-buffering *2,
1312          * The Dummy backend *1, and who knows what ASIO really does :)
1313          *
1314          * So just display the period size, that's also what
1315          * ARDOUR_UI::update_sample_rate() does for the status bar.
1316          * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1317          * but still, that's the buffer period, not [round-trip] latency)
1318          */
1319         char buf[32];
1320         snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1321         buffer_size_duration_label.set_text (buf);
1322 }
1323
1324 void
1325 EngineControl::midi_option_changed ()
1326 {
1327         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1328         assert (backend);
1329
1330         backend->set_midi_option (get_midi_option());
1331
1332         vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1333
1334         //_midi_devices.clear(); // TODO merge with state-saved settings..
1335         _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1336         std::vector<MidiDeviceSettings> new_devices;
1337
1338         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1339                 MidiDeviceSettings mds = find_midi_device (i->name);
1340                 if (i->available && !mds) {
1341                         uint32_t input_latency = 0;
1342                         uint32_t output_latency = 0;
1343                         if (_can_set_midi_latencies) {
1344                                 input_latency = backend->systemic_midi_input_latency (i->name);
1345                                 output_latency = backend->systemic_midi_output_latency (i->name);
1346                         }
1347                         bool enabled = backend->midi_device_enabled (i->name);
1348                         MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1349                         new_devices.push_back (ptr);
1350                 } else if (i->available) {
1351                         new_devices.push_back (mds);
1352                 }
1353         }
1354         _midi_devices = new_devices;
1355
1356         if (_midi_devices.empty()) {
1357                 midi_devices_button.set_sensitive (false);
1358         } else {
1359                 midi_devices_button.set_sensitive (true);
1360         }
1361 }
1362
1363 void
1364 EngineControl::parameter_changed ()
1365 {
1366 }
1367
1368 EngineControl::State
1369 EngineControl::get_matching_state (
1370                 const string& backend,
1371                 const string& driver,
1372                 const string& device)
1373 {
1374         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1375                 if ((*i)->backend == backend &&
1376                                 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1377                 {
1378                         return (*i);
1379                 }
1380         }
1381         return State();
1382 }
1383
1384 EngineControl::State
1385 EngineControl::get_matching_state (
1386                 const string& backend,
1387                 const string& driver,
1388                 const string& input_device,
1389                 const string& output_device)
1390 {
1391         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1392                 if ((*i)->backend == backend &&
1393                                 (!_have_control || ((*i)->driver == driver && ((*i)->input_device == input_device) && (*i)->output_device == output_device)))
1394                 {
1395                         return (*i);
1396                 }
1397         }
1398         return State();
1399 }
1400
1401 EngineControl::State
1402 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1403 {
1404         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1405
1406         if (backend) {
1407                 if (backend->use_separate_input_and_output_devices ()) {
1408                         return get_matching_state (backend_combo.get_active_text(),
1409                                         (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1410                                         input_device_combo.get_active_text(),
1411                                         output_device_combo.get_active_text());
1412                 } else {
1413                         return get_matching_state (backend_combo.get_active_text(),
1414                                         (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1415                                         device_combo.get_active_text());
1416                 }
1417         }
1418
1419         return get_matching_state (backend_combo.get_active_text(),
1420                         string(),
1421                         device_combo.get_active_text());
1422 }
1423
1424 bool EngineControl::equivalent_states (const EngineControl::State& state1,
1425                                        const EngineControl::State& state2)
1426 {
1427         if (state1->backend == state2->backend &&
1428                         state1->driver == state2->driver &&
1429                         state1->device == state2->device &&
1430                         state1->input_device == state2->input_device &&
1431                         state1->output_device == state2->output_device) {
1432                 return true;
1433         }
1434         return false;
1435 }
1436
1437 EngineControl::State
1438 EngineControl::save_state ()
1439 {
1440         State state;
1441
1442         if (!_have_control) {
1443                 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1444                 if (state) {
1445                         return state;
1446                 }
1447                 state.reset(new StateStruct);
1448                 state->backend = get_backend ();
1449         } else {
1450                 state.reset(new StateStruct);
1451                 store_state (state);
1452         }
1453
1454         for (StateList::iterator i = states.begin(); i != states.end();) {
1455                 if (equivalent_states (*i, state)) {
1456                         i =  states.erase(i);
1457                 } else {
1458                         ++i;
1459                 }
1460         }
1461
1462         states.push_back (state);
1463
1464         return state;
1465 }
1466
1467 void
1468 EngineControl::store_state (State state)
1469 {
1470         state->backend = get_backend ();
1471         state->driver = get_driver ();
1472         state->device = get_device_name ();
1473         state->input_device = get_input_device_name ();
1474         state->output_device = get_output_device_name ();
1475         state->sample_rate = get_rate ();
1476         state->buffer_size = get_buffer_size ();
1477         state->input_latency = get_input_latency ();
1478         state->output_latency = get_output_latency ();
1479         state->input_channels = get_input_channels ();
1480         state->output_channels = get_output_channels ();
1481         state->midi_option = get_midi_option ();
1482         state->midi_devices = _midi_devices;
1483 }
1484
1485 void
1486 EngineControl::maybe_display_saved_state ()
1487 {
1488         if (!_have_control) {
1489                 return;
1490         }
1491
1492         State state = get_saved_state_for_currently_displayed_backend_and_device ();
1493
1494         if (state) {
1495                 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1496
1497                 if (!_desired_sample_rate) {
1498                         sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1499                 }
1500                 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1501                 /* call this explicitly because we're ignoring changes to
1502                    the controls at this point.
1503                  */
1504                 show_buffer_duration ();
1505                 input_latency.set_value (state->input_latency);
1506                 output_latency.set_value (state->output_latency);
1507
1508                 if (!state->midi_option.empty()) {
1509                         midi_option_combo.set_active_text (state->midi_option);
1510                         _midi_devices = state->midi_devices;
1511                 }
1512         }
1513 }
1514
1515 XMLNode&
1516 EngineControl::get_state ()
1517 {
1518         XMLNode* root = new XMLNode ("AudioMIDISetup");
1519         std::string path;
1520
1521         if (!states.empty()) {
1522                 XMLNode* state_nodes = new XMLNode ("EngineStates");
1523
1524                 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1525
1526                         XMLNode* node = new XMLNode ("State");
1527
1528                         node->add_property ("backend", (*i)->backend);
1529                         node->add_property ("driver", (*i)->driver);
1530                         node->add_property ("device", (*i)->device);
1531                         node->add_property ("input-device", (*i)->input_device);
1532                         node->add_property ("output-device", (*i)->output_device);
1533                         node->add_property ("sample-rate", (*i)->sample_rate);
1534                         node->add_property ("buffer-size", (*i)->buffer_size);
1535                         node->add_property ("input-latency", (*i)->input_latency);
1536                         node->add_property ("output-latency", (*i)->output_latency);
1537                         node->add_property ("input-channels", (*i)->input_channels);
1538                         node->add_property ("output-channels", (*i)->output_channels);
1539                         node->add_property ("active", (*i)->active ? "yes" : "no");
1540                         node->add_property ("midi-option", (*i)->midi_option);
1541
1542                         XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1543                         for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1544                                 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1545                                 midi_device_stuff->add_property (X_("name"), (*p)->name);
1546                                 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1547                                 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1548                                 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1549                                 midi_devices->add_child_nocopy (*midi_device_stuff);
1550                         }
1551                         node->add_child_nocopy (*midi_devices);
1552
1553                         state_nodes->add_child_nocopy (*node);
1554                 }
1555
1556                 root->add_child_nocopy (*state_nodes);
1557         }
1558
1559         return *root;
1560 }
1561
1562 void
1563 EngineControl::set_state (const XMLNode& root)
1564 {
1565         XMLNodeList          clist, cclist;
1566         XMLNodeConstIterator citer, cciter;
1567         XMLNode* child;
1568         XMLNode* grandchild;
1569         XMLProperty* prop = NULL;
1570
1571         fprintf (stderr, "EngineControl::set_state\n");
1572
1573         if (root.name() != "AudioMIDISetup") {
1574                 return;
1575         }
1576
1577         clist = root.children();
1578
1579         states.clear ();
1580
1581         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1582
1583                 child = *citer;
1584
1585                 if (child->name() != "EngineStates") {
1586                         continue;
1587                 }
1588
1589                 cclist = child->children();
1590
1591                 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1592                         State state (new StateStruct);
1593
1594                         grandchild = *cciter;
1595
1596                         if (grandchild->name() != "State") {
1597                                 continue;
1598                         }
1599
1600                         if ((prop = grandchild->property ("backend")) == 0) {
1601                                 continue;
1602                         }
1603                         state->backend = prop->value ();
1604
1605                         if ((prop = grandchild->property ("driver")) == 0) {
1606                                 continue;
1607                         }
1608                         state->driver = prop->value ();
1609
1610                         if ((prop = grandchild->property ("device")) == 0) {
1611                                 continue;
1612                         }
1613                         state->device = prop->value ();
1614
1615                         if ((prop = grandchild->property ("input-device")) == 0) {
1616                                 continue;
1617                         }
1618                         state->input_device = prop->value ();
1619
1620                         if ((prop = grandchild->property ("output-device")) == 0) {
1621                                 continue;
1622                         }
1623                         state->output_device = prop->value ();
1624
1625                         if ((prop = grandchild->property ("sample-rate")) == 0) {
1626                                 continue;
1627                         }
1628                         state->sample_rate = atof (prop->value ());
1629
1630                         if ((prop = grandchild->property ("buffer-size")) == 0) {
1631                                 continue;
1632                         }
1633                         state->buffer_size = atoi (prop->value ());
1634
1635                         if ((prop = grandchild->property ("input-latency")) == 0) {
1636                                 continue;
1637                         }
1638                         state->input_latency = atoi (prop->value ());
1639
1640                         if ((prop = grandchild->property ("output-latency")) == 0) {
1641                                 continue;
1642                         }
1643                         state->output_latency = atoi (prop->value ());
1644
1645                         if ((prop = grandchild->property ("input-channels")) == 0) {
1646                                 continue;
1647                         }
1648                         state->input_channels = atoi (prop->value ());
1649
1650                         if ((prop = grandchild->property ("output-channels")) == 0) {
1651                                 continue;
1652                         }
1653                         state->output_channels = atoi (prop->value ());
1654
1655                         if ((prop = grandchild->property ("active")) == 0) {
1656                                 continue;
1657                         }
1658                         state->active = string_is_affirmative (prop->value ());
1659
1660                         if ((prop = grandchild->property ("midi-option")) == 0) {
1661                                 continue;
1662                         }
1663                         state->midi_option = prop->value ();
1664
1665                         state->midi_devices.clear();
1666                         XMLNode* midinode;
1667                         if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1668                                 const XMLNodeList mnc = midinode->children();
1669                                 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1670                                         if ((*n)->property (X_("name")) == 0
1671                                                         || (*n)->property (X_("enabled")) == 0
1672                                                         || (*n)->property (X_("input-latency")) == 0
1673                                                         || (*n)->property (X_("output-latency")) == 0
1674                                                  ) {
1675                                                 continue;
1676                                         }
1677
1678                                         MidiDeviceSettings ptr (new MidiDeviceSetting(
1679                                                                 (*n)->property (X_("name"))->value (),
1680                                                                 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1681                                                                 atoi ((*n)->property (X_("input-latency"))->value ()),
1682                                                                 atoi ((*n)->property (X_("output-latency"))->value ())
1683                                                                 ));
1684                                         state->midi_devices.push_back (ptr);
1685                                 }
1686                         }
1687
1688 #if 1
1689                         /* remove accumulated duplicates (due to bug in ealier version)
1690                          * this can be removed again before release
1691                          */
1692                         for (StateList::iterator i = states.begin(); i != states.end();) {
1693                                 if ((*i)->backend == state->backend &&
1694                                                 (*i)->driver == state->driver &&
1695                                                 (*i)->device == state->device) {
1696                                         i =  states.erase(i);
1697                                 } else {
1698                                         ++i;
1699                                 }
1700                         }
1701 #endif
1702
1703                         states.push_back (state);
1704                 }
1705         }
1706
1707         /* now see if there was an active state and switch the setup to it */
1708
1709         // purge states of backend that are not available in this built
1710         vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1711         vector<std::string> backend_names;
1712
1713         for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1714                 backend_names.push_back((*i)->name);
1715         }
1716         for (StateList::iterator i = states.begin(); i != states.end();) {
1717                 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1718                         i = states.erase(i);
1719                 } else {
1720                         ++i;
1721                 }
1722         }
1723
1724         for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1725
1726                 if ((*i)->active) {
1727                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1728                         backend_combo.set_active_text ((*i)->backend);
1729                         driver_combo.set_active_text ((*i)->driver);
1730                         device_combo.set_active_text ((*i)->device);
1731                         fprintf (stderr, "setting input device to: %s ", (*i)->input_device.c_str());
1732                         input_device_combo.set_active_text ((*i)->input_device);
1733                         fprintf (stderr, "setting output device to: %s ", (*i)->output_device.c_str());
1734                         output_device_combo.set_active_text ((*i)->output_device);
1735                         sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1736                         set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
1737                         input_latency.set_value ((*i)->input_latency);
1738                         output_latency.set_value ((*i)->output_latency);
1739                         midi_option_combo.set_active_text ((*i)->midi_option);
1740                         break;
1741                 }
1742         }
1743 }
1744
1745 int
1746 EngineControl::push_state_to_backend (bool start)
1747 {
1748         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1749
1750         if (!backend) {
1751                 return 0;
1752         }
1753
1754         /* figure out what is going to change */
1755
1756         bool restart_required = false;
1757         bool was_running = ARDOUR::AudioEngine::instance()->running();
1758         bool change_driver = false;
1759         bool change_device = false;
1760         bool change_rate = false;
1761         bool change_bufsize = false;
1762         bool change_latency = false;
1763         bool change_channels = false;
1764         bool change_midi = false;
1765
1766         uint32_t ochan = get_output_channels ();
1767         uint32_t ichan = get_input_channels ();
1768
1769         if (_have_control) {
1770
1771                 if (started_at_least_once) {
1772
1773                         /* we can control the backend */
1774
1775                         if (backend->requires_driver_selection()) {
1776                                 if (get_driver() != backend->driver_name()) {
1777                                         change_driver = true;
1778                                 }
1779                         }
1780
1781                         if (backend->use_separate_input_and_output_devices()) {
1782                                 if (get_input_device_name() != backend->input_device_name()) {
1783                                         change_device = true;
1784                                 }
1785                                 if (get_output_device_name() != backend->output_device_name()) {
1786                                         change_device = true;
1787                                 }
1788                         } else {
1789                                 if (get_device_name() != backend->device_name()) {
1790                                         change_device = true;
1791                                 }
1792                         }
1793
1794                         if (queue_device_changed) {
1795                                 change_device = true;
1796                         }
1797
1798                         if (get_rate() != backend->sample_rate()) {
1799                                 change_rate = true;
1800                         }
1801
1802                         if (get_buffer_size() != backend->buffer_size()) {
1803                                 change_bufsize = true;
1804                         }
1805
1806                         if (get_midi_option() != backend->midi_option()) {
1807                                 change_midi = true;
1808                         }
1809
1810                         /* zero-requested channels means "all available" */
1811
1812                         if (ichan == 0) {
1813                                 ichan = backend->input_channels();
1814                         }
1815
1816                         if (ochan == 0) {
1817                                 ochan = backend->output_channels();
1818                         }
1819
1820                         if (ichan != backend->input_channels()) {
1821                                 change_channels = true;
1822                         }
1823
1824                         if (ochan != backend->output_channels()) {
1825                                 change_channels = true;
1826                         }
1827
1828                         if (get_input_latency() != backend->systemic_input_latency() ||
1829                                         get_output_latency() != backend->systemic_output_latency()) {
1830                                 change_latency = true;
1831                         }
1832                 } else {
1833                         /* backend never started, so we have to force a group
1834                            of settings.
1835                          */
1836                         change_device = true;
1837                         if (backend->requires_driver_selection()) {
1838                                 change_driver = true;
1839                         }
1840                         change_rate = true;
1841                         change_bufsize = true;
1842                         change_channels = true;
1843                         change_latency = true;
1844                         change_midi = true;
1845                 }
1846
1847         } else {
1848
1849                 /* we have no control over the backend, meaning that we can
1850                  * only possibly change sample rate and buffer size.
1851                  */
1852
1853
1854                 if (get_rate() != backend->sample_rate()) {
1855                         change_bufsize = true;
1856                 }
1857
1858                 if (get_buffer_size() != backend->buffer_size()) {
1859                         change_bufsize = true;
1860                 }
1861         }
1862
1863         queue_device_changed = false;
1864
1865         if (!_have_control) {
1866
1867                 /* We do not have control over the backend, so the best we can
1868                  * do is try to change the sample rate and/or bufsize and get
1869                  * out of here.
1870                  */
1871
1872                 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1873                         return 1;
1874                 }
1875
1876                 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1877                         return 1;
1878                 }
1879
1880                 if (change_rate) {
1881                         backend->set_sample_rate (get_rate());
1882                 }
1883
1884                 if (change_bufsize) {
1885                         backend->set_buffer_size (get_buffer_size());
1886                 }
1887
1888                 if (start) {
1889                         if (ARDOUR::AudioEngine::instance()->start ()) {
1890                                 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1891                                 return -1;
1892                         }
1893                 }
1894
1895                 post_push ();
1896
1897                 return 0;
1898         }
1899
1900         /* determine if we need to stop the backend before changing parameters */
1901
1902         if (change_driver || change_device || change_channels || change_latency ||
1903                         (change_rate && !backend->can_change_sample_rate_when_running()) ||
1904                         change_midi ||
1905                         (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1906                 restart_required = true;
1907         } else {
1908                 restart_required = false;
1909         }
1910
1911         if (was_running) {
1912
1913                 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1914                         /* no changes in any parameters that absolutely require a
1915                          * restart, so check those that might be changeable without a
1916                          * restart
1917                          */
1918
1919                         if (change_rate && !backend->can_change_sample_rate_when_running()) {
1920                                 /* can't do this while running ... */
1921                                 restart_required = true;
1922                         }
1923
1924                         if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1925                                 /* can't do this while running ... */
1926                                 restart_required = true;
1927                         }
1928                 }
1929         }
1930
1931         if (was_running) {
1932                 if (restart_required) {
1933                         if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1934                                 return -1;
1935                         }
1936                 }
1937         }
1938
1939
1940         if (change_driver && backend->set_driver (get_driver())) {
1941                 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1942                 return -1;
1943         }
1944         if (backend->use_separate_input_and_output_devices()) {
1945                 if (change_device && backend->set_input_device_name (get_input_device_name())) {
1946                         error << string_compose (_("Cannot set input device name to %1"), get_input_device_name()) << endmsg;
1947                         return -1;
1948                 }
1949                 if (change_device && backend->set_output_device_name (get_output_device_name())) {
1950                         error << string_compose (_("Cannot set output device name to %1"), get_output_device_name()) << endmsg;
1951                         return -1;
1952                 }
1953         } else {
1954                 if (change_device && backend->set_device_name (get_device_name())) {
1955                         error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1956                         return -1;
1957                 }
1958         }
1959         if (change_rate && backend->set_sample_rate (get_rate())) {
1960                 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1961                 return -1;
1962         }
1963         if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1964                 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1965                 return -1;
1966         }
1967
1968         if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1969                 if (backend->set_input_channels (get_input_channels())) {
1970                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1971                         return -1;
1972                 }
1973                 if (backend->set_output_channels (get_output_channels())) {
1974                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1975                         return -1;
1976                 }
1977         }
1978         if (change_latency) {
1979                 if (backend->set_systemic_input_latency (get_input_latency())) {
1980                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1981                         return -1;
1982                 }
1983                 if (backend->set_systemic_output_latency (get_output_latency())) {
1984                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1985                         return -1;
1986                 }
1987         }
1988
1989         if (change_midi) {
1990                 backend->set_midi_option (get_midi_option());
1991         }
1992
1993         if (1 /* TODO */) {
1994                 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1995                         if (_measure_midi) {
1996                                 if (*p == _measure_midi) {
1997                                         backend->set_midi_device_enabled ((*p)->name, true);
1998                                 } else {
1999                                         backend->set_midi_device_enabled ((*p)->name, false);
2000                                 }
2001                                 continue;
2002                         }
2003                         backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
2004                         if (backend->can_set_systemic_midi_latencies()) {
2005                                 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
2006                                 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
2007                         }
2008                 }
2009         }
2010
2011         if (start || (was_running && restart_required)) {
2012                 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
2013                         return -1;
2014                 }
2015         }
2016
2017         post_push ();
2018
2019         return 0;
2020 }
2021
2022 void
2023 EngineControl::post_push ()
2024 {
2025         /* get a pointer to the current state object, creating one if
2026          * necessary
2027          */
2028
2029         State state = get_saved_state_for_currently_displayed_backend_and_device ();
2030
2031         if (!state) {
2032                 state = save_state ();
2033                 assert (state);
2034         } else {
2035                 store_state(state);
2036         }
2037
2038         /* all off */
2039
2040         for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
2041                 (*i)->active = false;
2042         }
2043
2044         /* mark this one active (to be used next time the dialog is
2045          * shown)
2046          */
2047
2048         state->active = true;
2049
2050         if (_have_control) { // XXX
2051                 manage_control_app_sensitivity ();
2052         }
2053
2054         /* schedule a redisplay of MIDI ports */
2055         //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
2056 }
2057
2058
2059 float
2060 EngineControl::get_rate () const
2061 {
2062         float r = atof (sample_rate_combo.get_active_text ());
2063         /* the string may have been translated with an abbreviation for
2064          * thousands, so use a crude heuristic to fix this.
2065          */
2066         if (r < 1000.0) {
2067                 r *= 1000.0;
2068         }
2069         return r;
2070 }
2071
2072
2073 uint32_t
2074 EngineControl::get_buffer_size () const
2075 {
2076         string txt = buffer_size_combo.get_active_text ();
2077         uint32_t samples;
2078
2079         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
2080                 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
2081                 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
2082                 throw exception ();
2083         }
2084
2085         return samples;
2086 }
2087
2088 string
2089 EngineControl::get_midi_option () const
2090 {
2091         return midi_option_combo.get_active_text();
2092 }
2093
2094 uint32_t
2095 EngineControl::get_input_channels() const
2096 {
2097         if (ARDOUR::Profile->get_mixbus()) {
2098                 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2099                 if (!backend) return 0;
2100                 return backend->input_channels();
2101         }
2102         return (uint32_t) input_channels_adjustment.get_value();
2103 }
2104
2105 uint32_t
2106 EngineControl::get_output_channels() const
2107 {
2108         if (ARDOUR::Profile->get_mixbus()) {
2109                 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2110                 if (!backend) return 0;
2111                 return backend->input_channels();
2112         }
2113         return (uint32_t) output_channels_adjustment.get_value();
2114 }
2115
2116 uint32_t
2117 EngineControl::get_input_latency() const
2118 {
2119         return (uint32_t) input_latency_adjustment.get_value();
2120 }
2121
2122 uint32_t
2123 EngineControl::get_output_latency() const
2124 {
2125         return (uint32_t) output_latency_adjustment.get_value();
2126 }
2127
2128 string
2129 EngineControl::get_backend () const
2130 {
2131         return backend_combo.get_active_text ();
2132 }
2133
2134 string
2135 EngineControl::get_driver () const
2136 {
2137         if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
2138                 return driver_combo.get_active_text ();
2139         } else {
2140                 return "";
2141         }
2142 }
2143
2144 string
2145 EngineControl::get_device_name () const
2146 {
2147         return device_combo.get_active_text ();
2148 }
2149
2150 string
2151 EngineControl::get_input_device_name () const
2152 {
2153         return input_device_combo.get_active_text ();
2154 }
2155
2156 string
2157 EngineControl::get_output_device_name () const
2158 {
2159         return output_device_combo.get_active_text ();
2160 }
2161
2162 void
2163 EngineControl::control_app_button_clicked ()
2164 {
2165         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2166
2167         if (!backend) {
2168                 return;
2169         }
2170
2171         backend->launch_control_app ();
2172 }
2173
2174 void
2175 EngineControl::manage_control_app_sensitivity ()
2176 {
2177         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2178
2179         if (!backend) {
2180                 return;
2181         }
2182
2183         string appname = backend->control_app_name();
2184
2185         if (appname.empty()) {
2186                 control_app_button.set_sensitive (false);
2187         } else {
2188                 control_app_button.set_sensitive (true);
2189         }
2190 }
2191
2192 void
2193 EngineControl::set_desired_sample_rate (uint32_t sr)
2194 {
2195         _desired_sample_rate = sr;
2196         device_changed ();
2197         input_device_changed ();
2198         output_device_changed ();
2199 }
2200
2201 void
2202 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
2203 {
2204         if (page_num == 0) {
2205                 cancel_button->set_sensitive (true);
2206                 ok_button->set_sensitive (true);
2207                 apply_button->set_sensitive (true);
2208                 _measure_midi.reset();
2209         } else {
2210                 cancel_button->set_sensitive (false);
2211                 ok_button->set_sensitive (false);
2212                 apply_button->set_sensitive (false);
2213         }
2214
2215         if (page_num == midi_tab) {
2216                 /* MIDI tab */
2217                 refresh_midi_display ();
2218         }
2219
2220         if (page_num == latency_tab) {
2221                 /* latency tab */
2222
2223                 if (ARDOUR::AudioEngine::instance()->running()) {
2224                         // TODO - mark as 'stopped for latency
2225                         ARDOUR_UI::instance()->disconnect_from_engine ();
2226                 }
2227
2228                 {
2229                         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
2230
2231                         /* save any existing latency values */
2232
2233                         uint32_t il = (uint32_t) input_latency.get_value ();
2234                         uint32_t ol = (uint32_t) input_latency.get_value ();
2235
2236                         /* reset to zero so that our new test instance
2237                            will be clean of any existing latency measures.
2238
2239                            NB. this should really be done by the backend
2240                            when stated for latency measurement.
2241                         */
2242
2243                         input_latency.set_value (0);
2244                         output_latency.set_value (0);
2245
2246                         push_state_to_backend (false);
2247
2248                         /* reset control */
2249
2250                         input_latency.set_value (il);
2251                         output_latency.set_value (ol);
2252
2253                 }
2254                 // This should be done in push_state_to_backend()
2255                 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
2256                         disable_latency_tab ();
2257                 }
2258
2259                 enable_latency_tab ();
2260
2261         } else {
2262                 if (lm_running) {
2263                         end_latency_detection ();
2264                         ARDOUR::AudioEngine::instance()->stop_latency_detection();
2265                 }
2266         }
2267 }
2268
2269 /* latency measurement */
2270
2271 bool
2272 EngineControl::check_audio_latency_measurement ()
2273 {
2274         MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2275
2276         if (mtdm->resolve () < 0) {
2277                 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2278                 return true;
2279         }
2280
2281         if (mtdm->err () > 0.3) {
2282                 mtdm->invert ();
2283                 mtdm->resolve ();
2284         }
2285
2286         char buf[256];
2287         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2288
2289         if (sample_rate == 0) {
2290                 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2291                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2292                 return false;
2293         }
2294
2295         int frames_total = mtdm->del();
2296         int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2297
2298         snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
2299                         _("Detected roundtrip latency: "),
2300                         frames_total, frames_total * 1000.0f/sample_rate,
2301                         _("Systemic latency: "),
2302                         extra, extra * 1000.0f/sample_rate);
2303
2304         bool solid = true;
2305
2306         if (mtdm->err () > 0.2) {
2307                 strcat (buf, " ");
2308                 strcat (buf, _("(signal detection error)"));
2309                 solid = false;
2310         }
2311
2312         if (mtdm->inv ()) {
2313                 strcat (buf, " ");
2314                 strcat (buf, _("(inverted - bad wiring)"));
2315                 solid = false;
2316         }
2317
2318         lm_results.set_markup (string_compose (results_markup, buf));
2319
2320         if (solid) {
2321                 have_lm_results = true;
2322                 end_latency_detection ();
2323                 lm_use_button.set_sensitive (true);
2324                 return false;
2325         }
2326
2327         return true;
2328 }
2329
2330 bool
2331 EngineControl::check_midi_latency_measurement ()
2332 {
2333         ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2334
2335         if (!mididm->have_signal () || mididm->latency () == 0) {
2336                 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2337                 return true;
2338         }
2339
2340         char buf[256];
2341         ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2342
2343         if (sample_rate == 0) {
2344                 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2345                 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2346                 return false;
2347         }
2348
2349         ARDOUR::framecnt_t frames_total = mididm->latency();
2350         ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2351         snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2352                         _("Detected roundtrip latency: "),
2353                         frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2354                         _("Systemic latency: "),
2355                         extra, extra * 1000.0f / sample_rate);
2356
2357         bool solid = true;
2358
2359         if (!mididm->ok ()) {
2360                 strcat (buf, " ");
2361                 strcat (buf, _("(averaging)"));
2362                 solid = false;
2363         }
2364
2365         if (mididm->deviation () > 50.0) {
2366                 strcat (buf, " ");
2367                 strcat (buf, _("(too large jitter)"));
2368                 solid = false;
2369         } else if (mididm->deviation () > 10.0) {
2370                 strcat (buf, " ");
2371                 strcat (buf, _("(large jitter)"));
2372         }
2373
2374         if (solid) {
2375                 have_lm_results = true;
2376                 end_latency_detection ();
2377                 lm_use_button.set_sensitive (true);
2378                 lm_results.set_markup (string_compose (results_markup, buf));
2379                 return false;
2380         } else if (mididm->processed () > 400) {
2381                 have_lm_results = false;
2382                 end_latency_detection ();
2383                 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2384                 return false;
2385         }
2386
2387         lm_results.set_markup (string_compose (results_markup, buf));
2388
2389         return true;
2390 }
2391
2392 void
2393 EngineControl::start_latency_detection ()
2394 {
2395         ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2396         ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2397
2398         if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2399                 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2400                 if (_measure_midi) {
2401                         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2402                 } else {
2403                         latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2404                 }
2405                 lm_measure_label.set_text (_("Cancel"));
2406                 have_lm_results = false;
2407                 lm_use_button.set_sensitive (false);
2408                 lm_input_channel_combo.set_sensitive (false);
2409                 lm_output_channel_combo.set_sensitive (false);
2410                 lm_running = true;
2411         }
2412 }
2413
2414 void
2415 EngineControl::end_latency_detection ()
2416 {
2417         latency_timeout.disconnect ();
2418         ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2419         lm_measure_label.set_text (_("Measure"));
2420         if (!have_lm_results) {
2421                 lm_use_button.set_sensitive (false);
2422         }
2423         lm_input_channel_combo.set_sensitive (true);
2424         lm_output_channel_combo.set_sensitive (true);
2425         lm_running = false;
2426 }
2427
2428 void
2429 EngineControl::latency_button_clicked ()
2430 {
2431         if (!lm_running) {
2432                 start_latency_detection ();
2433         } else {
2434                 end_latency_detection ();
2435         }
2436 }
2437
2438 void
2439 EngineControl::use_latency_button_clicked ()
2440 {
2441         if (_measure_midi) {
2442                 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2443                 if (!mididm) {
2444                         return;
2445                 }
2446                 ARDOUR::framecnt_t frames_total = mididm->latency();
2447                 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2448                 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2449                 _measure_midi->input_latency = one_way;
2450                 _measure_midi->output_latency = one_way;
2451                 notebook.set_current_page (midi_tab);
2452         } else {
2453                 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2454
2455                 if (!mtdm) {
2456                         return;
2457                 }
2458
2459                 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2460                 one_way = std::max (0., one_way);
2461
2462                 input_latency_adjustment.set_value (one_way);
2463                 output_latency_adjustment.set_value (one_way);
2464
2465                 /* back to settings page */
2466                 notebook.set_current_page (0);
2467 }
2468         }
2469
2470
2471 bool
2472 EngineControl::on_delete_event (GdkEventAny* ev)
2473 {
2474         if (notebook.get_current_page() == 2) {
2475                 /* currently on latency tab - be sure to clean up */
2476                 end_latency_detection ();
2477         }
2478         return ArdourDialog::on_delete_event (ev);
2479 }
2480
2481 void
2482 EngineControl::engine_running ()
2483 {
2484         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2485         assert (backend);
2486
2487         set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2488         sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2489
2490         buffer_size_combo.set_sensitive (true);
2491         sample_rate_combo.set_sensitive (true);
2492
2493         connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2494         connect_disconnect_button.show();
2495
2496         started_at_least_once = true;
2497         engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2498 }
2499
2500 void
2501 EngineControl::engine_stopped ()
2502 {
2503         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2504         assert (backend);
2505
2506         buffer_size_combo.set_sensitive (false);
2507         connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2508         connect_disconnect_button.show();
2509
2510         sample_rate_combo.set_sensitive (true);
2511         buffer_size_combo.set_sensitive (true);
2512         engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2513 }
2514
2515 void
2516 EngineControl::device_list_changed ()
2517 {
2518         PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1); // ??
2519         list_devices ();
2520         midi_option_changed();
2521 }
2522
2523 void
2524 EngineControl::connect_disconnect_click()
2525 {
2526         if (ARDOUR::AudioEngine::instance()->running()) {
2527                 ARDOUR_UI::instance()->disconnect_from_engine ();
2528         } else {
2529                 ARDOUR_UI::instance()->reconnect_to_engine ();
2530         }
2531 }
2532
2533 void
2534 EngineControl::calibrate_audio_latency ()
2535 {
2536         _measure_midi.reset ();
2537         have_lm_results = false;
2538         lm_use_button.set_sensitive (false);
2539         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2540         notebook.set_current_page (latency_tab);
2541 }
2542
2543 void
2544 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2545 {
2546         _measure_midi = s;
2547         have_lm_results = false;
2548         lm_use_button.set_sensitive (false);
2549         lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2550         notebook.set_current_page (latency_tab);
2551 }
2552
2553 void
2554 EngineControl::configure_midi_devices ()
2555 {
2556         notebook.set_current_page (midi_tab);
2557 }