2 Copyright (C) 2010 Paul Davis
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.
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.
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.
26 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
30 #include "pbd/error.h"
31 #include "pbd/xml++.h"
32 #include "pbd/unwind.h"
33 #include "pbd/failed_constructor.h"
35 #include <gtkmm/alignment.h>
36 #include <gtkmm/stock.h>
37 #include <gtkmm/notebook.h>
38 #include <gtkmm2ext/utils.h>
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"
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
51 #include "ardour_ui.h"
52 #include "engine_dialog.h"
53 #include "gui_thread.h"
59 using namespace Gtkmm2ext;
62 using namespace ARDOUR_UI_UTILS;
64 static const unsigned int midi_tab = 2;
65 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
67 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
69 EngineControl::EngineControl ()
70 : ArdourDialog (_("Audio/MIDI Setup"))
73 , input_latency_adjustment (0, 0, 99999, 1)
74 , input_latency (input_latency_adjustment)
75 , output_latency_adjustment (0, 0, 99999, 1)
76 , output_latency (output_latency_adjustment)
77 , input_channels_adjustment (0, 0, 256, 1)
78 , input_channels (input_channels_adjustment)
79 , output_channels_adjustment (0, 0, 256, 1)
80 , output_channels (output_channels_adjustment)
81 , ports_adjustment (128, 8, 1024, 1, 16)
82 , ports_spinner (ports_adjustment)
83 , control_app_button (_("Device Control Panel"))
84 , midi_devices_button (_("Midi Device Setup"))
85 , lm_measure_label (_("Measure"))
86 , lm_use_button (_("Use results"))
87 , lm_back_button (_("Back to settings ... (ignore results)"))
88 , lm_button_audio (_("Calibrate Audio"))
90 , have_lm_results (false)
92 , midi_back_button (_("Back to settings"))
94 , _desired_sample_rate (0)
95 , started_at_least_once (false)
96 , queue_device_changed (false)
98 using namespace Notebook_Helpers;
99 vector<string> backend_names;
101 AttachOptions xopt = AttachOptions (FILL|EXPAND);
104 set_name (X_("AudioMIDISetup"));
106 /* the backend combo is the one thing that is ALWAYS visible */
108 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
110 if (backends.empty()) {
111 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 throw failed_constructor ();
116 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
117 backend_names.push_back ((*b)->name);
120 set_popdown_strings (backend_combo, backend_names);
121 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
123 /* setup basic packing characteristics for the table used on the main
124 * tab of the notebook
127 basic_packer.set_spacings (6);
128 basic_packer.set_border_width (12);
129 basic_packer.set_homogeneous (false);
133 basic_hbox.pack_start (basic_packer, false, false);
135 /* latency measurement tab */
137 lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
140 lm_table.set_row_spacings (12);
141 lm_table.set_col_spacings (6);
142 lm_table.set_homogeneous (false);
144 lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
147 lm_preamble.set_width_chars (60);
148 lm_preamble.set_line_wrap (true);
149 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
151 lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
154 Gtk::Label* preamble;
155 preamble = manage (new Label);
156 preamble->set_width_chars (60);
157 preamble->set_line_wrap (true);
158 preamble->set_markup (_("Select two channels below and connect them using a cable."));
160 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
163 label = manage (new Label (_("Output channel")));
164 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
166 Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
167 misc_align->add (lm_output_channel_combo);
168 lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
171 label = manage (new Label (_("Input channel")));
172 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
174 misc_align = manage (new Alignment (0.0, 0.5));
175 misc_align->add (lm_input_channel_combo);
176 lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
179 xopt = AttachOptions(0);
181 lm_measure_label.set_padding (10, 10);
182 lm_measure_button.add (lm_measure_label);
183 lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
184 lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
185 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
187 lm_use_button.set_sensitive (false);
189 /* Increase the default spacing around the labels of these three
195 if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
196 l->set_padding (10, 10);
199 if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
200 l->set_padding (10, 10);
203 preamble = manage (new Label);
204 preamble->set_width_chars (60);
205 preamble->set_line_wrap (true);
206 preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
207 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
210 preamble = manage (new Label);
211 preamble->set_width_chars (60);
212 preamble->set_line_wrap (true);
213 preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
214 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
216 ++row; // skip a row in the table
217 ++row; // skip a row in the table
219 lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
221 ++row; // skip a row in the table
222 ++row; // skip a row in the table
224 lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
225 lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
226 lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
228 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
230 lm_vbox.set_border_width (12);
231 lm_vbox.pack_start (lm_table, false, false);
233 midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
237 notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
238 notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
239 notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
240 notebook.set_border_width (12);
242 notebook.set_show_tabs (false);
243 notebook.show_all ();
245 notebook.set_name ("SettingsNotebook");
247 /* packup the notebook */
249 get_vbox()->set_border_width (12);
250 get_vbox()->pack_start (notebook);
252 get_action_area()->pack_start (engine_status);
253 engine_status.show();
255 /* need a special function to print "all available channels" when the
256 * channel counts hit zero.
259 input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
260 output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
262 midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
263 midi_devices_button.set_sensitive (false);
264 midi_devices_button.set_name ("generic button");
265 midi_devices_button.set_can_focus(true);
267 control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
268 manage_control_app_sensitivity ();
270 cancel_button = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL);
271 apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
272 ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
274 /* Pick up any existing audio setup configuration, if appropriate */
276 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
278 ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
279 ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
280 ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
281 ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
284 set_state (*audio_setup);
287 if (backend_combo.get_active_text().empty()) {
288 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
289 backend_combo.set_active_text (backend_names.front());
294 /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
295 if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
296 backend_combo.set_active_text (backend_names.back());
297 /* ignore: don't save state */
298 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
303 /* Connect to signals */
305 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
306 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
307 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
308 device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
309 midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
311 input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
312 output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
313 input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
314 output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
316 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
318 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
319 connect_disconnect_button.set_no_show_all();
324 EngineControl::on_show ()
326 ArdourDialog::on_show ();
328 ok_button->grab_focus();
332 EngineControl::on_response (int response_id)
334 ArdourDialog::on_response (response_id);
336 switch (response_id) {
338 push_state_to_backend (true);
341 push_state_to_backend (true);
344 case RESPONSE_DELETE_EVENT:
347 ev.type = GDK_BUTTON_PRESS;
349 on_delete_event ((GdkEventAny*) &ev);
358 EngineControl::build_notebook ()
361 AttachOptions xopt = AttachOptions (FILL|EXPAND);
363 /* clear the table */
365 Gtkmm2ext::container_clear (basic_vbox);
366 Gtkmm2ext::container_clear (basic_packer);
368 if (control_app_button.get_parent()) {
369 control_app_button.get_parent()->remove (control_app_button);
372 label = manage (left_aligned_label (_("Audio System:")));
373 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
374 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
376 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
377 lm_button_audio.set_name ("generic button");
378 lm_button_audio.set_can_focus(true);
381 build_full_control_notebook ();
383 build_no_control_notebook ();
386 basic_vbox.pack_start (basic_hbox, false, false);
389 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
390 basic_vbox.show_all ();
395 EngineControl::build_full_control_notebook ()
397 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
400 using namespace Notebook_Helpers;
402 vector<string> strings;
403 AttachOptions xopt = AttachOptions (FILL|EXPAND);
404 int row = 1; // row zero == backend combo
406 /* start packing it up */
408 if (backend->requires_driver_selection()) {
409 label = manage (left_aligned_label (_("Driver:")));
410 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
411 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
415 label = manage (left_aligned_label (_("Device:")));
416 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
417 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
420 label = manage (left_aligned_label (_("Sample rate:")));
421 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
422 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
426 label = manage (left_aligned_label (_("Buffer size:")));
427 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
428 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
429 buffer_size_duration_label.set_alignment (0.0); /* left-align */
430 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
432 /* button spans 2 rows */
434 basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
437 input_channels.set_name ("InputChannels");
438 input_channels.set_flags (Gtk::CAN_FOCUS);
439 input_channels.set_digits (0);
440 input_channels.set_wrap (false);
441 output_channels.set_editable (true);
443 if (!ARDOUR::Profile->get_mixbus()) {
444 label = manage (left_aligned_label (_("Input Channels:")));
445 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
446 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
450 output_channels.set_name ("OutputChannels");
451 output_channels.set_flags (Gtk::CAN_FOCUS);
452 output_channels.set_digits (0);
453 output_channels.set_wrap (false);
454 output_channels.set_editable (true);
456 if (!ARDOUR::Profile->get_mixbus()) {
457 label = manage (left_aligned_label (_("Output Channels:")));
458 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
459 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
463 input_latency.set_name ("InputLatency");
464 input_latency.set_flags (Gtk::CAN_FOCUS);
465 input_latency.set_digits (0);
466 input_latency.set_wrap (false);
467 input_latency.set_editable (true);
469 label = manage (left_aligned_label (_("Hardware input latency:")));
470 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
471 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
472 label = manage (left_aligned_label (_("samples")));
473 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
476 output_latency.set_name ("OutputLatency");
477 output_latency.set_flags (Gtk::CAN_FOCUS);
478 output_latency.set_digits (0);
479 output_latency.set_wrap (false);
480 output_latency.set_editable (true);
482 label = manage (left_aligned_label (_("Hardware output latency:")));
483 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
484 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
485 label = manage (left_aligned_label (_("samples")));
486 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
488 /* button spans 2 rows */
490 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
493 label = manage (left_aligned_label (_("MIDI System:")));
494 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
495 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
496 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
501 EngineControl::build_no_control_notebook ()
503 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
506 using namespace Notebook_Helpers;
508 vector<string> strings;
509 AttachOptions xopt = AttachOptions (FILL|EXPAND);
510 int row = 1; // row zero == backend combo
511 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
513 label = manage (new Label);
514 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
515 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
518 if (backend->can_change_sample_rate_when_running()) {
519 label = manage (left_aligned_label (_("Sample rate:")));
520 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
521 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
525 if (backend->can_change_buffer_size_when_running()) {
526 label = manage (left_aligned_label (_("Buffer size:")));
527 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
528 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
529 buffer_size_duration_label.set_alignment (0.0); /* left-align */
530 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
534 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
538 EngineControl::~EngineControl ()
540 ignore_changes = true;
544 EngineControl::disable_latency_tab ()
546 vector<string> empty;
547 set_popdown_strings (lm_output_channel_combo, empty);
548 set_popdown_strings (lm_input_channel_combo, empty);
549 lm_measure_button.set_sensitive (false);
550 lm_use_button.set_sensitive (false);
554 EngineControl::enable_latency_tab ()
556 vector<string> outputs;
557 vector<string> inputs;
559 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
560 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
561 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
563 if (!ARDOUR::AudioEngine::instance()->running()) {
564 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
565 notebook.set_current_page (0);
569 else if (inputs.empty() || outputs.empty()) {
570 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
571 notebook.set_current_page (0);
576 lm_back_button_signal.disconnect();
578 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
581 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
585 set_popdown_strings (lm_output_channel_combo, outputs);
586 lm_output_channel_combo.set_active_text (outputs.front());
587 lm_output_channel_combo.set_sensitive (true);
589 set_popdown_strings (lm_input_channel_combo, inputs);
590 lm_input_channel_combo.set_active_text (inputs.front());
591 lm_input_channel_combo.set_sensitive (true);
593 lm_measure_button.set_sensitive (true);
597 EngineControl::setup_midi_tab_for_backend ()
599 string backend = backend_combo.get_active_text ();
601 Gtkmm2ext::container_clear (midi_vbox);
603 midi_vbox.set_border_width (12);
604 midi_device_table.set_border_width (12);
606 if (backend == "JACK") {
607 setup_midi_tab_for_jack ();
610 midi_vbox.pack_start (midi_device_table, true, true);
611 midi_vbox.pack_start (midi_back_button, false, false);
612 midi_vbox.show_all ();
616 EngineControl::setup_midi_tab_for_jack ()
621 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
623 device->input_latency = a->get_value();
625 device->output_latency = a->get_value();
630 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
631 b->set_active (!b->get_active());
632 device->enabled = b->get_active();
633 refresh_midi_display(device->name);
637 EngineControl::refresh_midi_display (std::string focus)
639 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
643 AttachOptions xopt = AttachOptions (FILL|EXPAND);
646 Gtkmm2ext::container_clear (midi_device_table);
648 midi_device_table.set_spacings (6);
650 l = manage (new Label);
651 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
652 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
653 l->set_alignment (0.5, 0.5);
657 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
658 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
659 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
660 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
662 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
663 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
664 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
665 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
668 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
673 bool enabled = (*p)->enabled;
675 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
676 m->set_name ("midi device");
677 m->set_can_focus (Gtk::CAN_FOCUS);
678 m->add_events (Gdk::BUTTON_RELEASE_MASK);
679 m->set_active (enabled);
680 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
681 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
682 if ((*p)->name == focus) {
686 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
687 s = manage (new Gtk::SpinButton (*a));
688 a->set_value ((*p)->input_latency);
689 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
690 s->set_sensitive (_can_set_midi_latencies && enabled);
691 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
693 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
694 s = manage (new Gtk::SpinButton (*a));
695 a->set_value ((*p)->output_latency);
696 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
697 s->set_sensitive (_can_set_midi_latencies && enabled);
698 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
700 b = manage (new Button (_("Calibrate")));
701 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
702 b->set_sensitive (_can_set_midi_latencies && enabled);
703 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
710 EngineControl::update_sensitivity ()
715 EngineControl::backend_changed ()
717 string backend_name = backend_combo.get_active_text();
718 boost::shared_ptr<ARDOUR::AudioBackend> backend;
720 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
721 /* eh? setting the backend failed... how ? */
722 /* A: stale config contains a backend that does not exist in current build */
726 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
729 setup_midi_tab_for_backend ();
730 _midi_devices.clear();
732 if (backend->requires_driver_selection()) {
733 vector<string> drivers = backend->enumerate_drivers();
734 driver_combo.set_sensitive (true);
736 if (!drivers.empty()) {
738 string current_driver;
739 current_driver = backend->driver_name ();
741 // driver might not have been set yet
742 if (current_driver == "") {
743 current_driver = driver_combo.get_active_text ();
744 if (current_driver == "")
745 // driver has never been set, make sure it's not blank
746 current_driver = drivers.front ();
749 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
750 set_popdown_strings (driver_combo, drivers);
751 driver_combo.set_active_text (current_driver);
758 driver_combo.set_sensitive (false);
759 /* this will change the device text which will cause a call to
760 * device changed which will set up parameters
765 vector<string> midi_options = backend->enumerate_midi_options();
767 if (midi_options.size() == 1) {
768 /* only contains the "none" option */
769 midi_option_combo.set_sensitive (false);
772 set_popdown_strings (midi_option_combo, midi_options);
773 midi_option_combo.set_active_text (midi_options.front());
774 midi_option_combo.set_sensitive (true);
776 midi_option_combo.set_sensitive (false);
780 connect_disconnect_button.hide();
782 midi_option_changed();
784 started_at_least_once = false;
786 if (!ignore_changes) {
787 maybe_display_saved_state ();
792 EngineControl::print_channel_count (Gtk::SpinButton* sb)
794 if (ARDOUR::Profile->get_mixbus()) {
798 uint32_t cnt = (uint32_t) sb->get_value();
800 sb->set_text (_("all available channels"));
803 snprintf (buf, sizeof (buf), "%d", cnt);
810 EngineControl::list_devices ()
812 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
815 /* now fill out devices, mark sample rates, buffer sizes insensitive */
817 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
819 /* NOTE: Ardour currently does not display the "available" field of the
822 * Doing so would require a different GUI widget than the combo
823 * box/popdown that we currently use, since it has no way to list
824 * items that are not selectable. Something more like a popup menu,
825 * which could have unselectable items, would be appropriate.
828 vector<string> available_devices;
830 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
831 available_devices.push_back (i->name);
834 if (!available_devices.empty()) {
836 update_sensitivity ();
839 string current_device, found_device;
840 current_device = device_combo.get_active_text ();
841 if (current_device == "") {
842 current_device = backend->device_name ();
845 // Make sure that the active text is still relevant for this
846 // device (it might only be relevant to the previous device!!)
847 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
848 if (*i == current_device)
849 found_device = current_device;
851 if (found_device == "")
852 // device has never been set (or was not relevant
853 // for this backend) Let's make sure it's not blank
854 current_device = available_devices.front ();
856 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
857 set_popdown_strings (device_combo, available_devices);
859 device_combo.set_active_text (current_device);
864 input_latency.set_sensitive (true);
865 output_latency.set_sensitive (true);
866 input_channels.set_sensitive (true);
867 output_channels.set_sensitive (true);
869 ok_button->set_sensitive (true);
870 apply_button->set_sensitive (true);
873 device_combo.clear();
874 sample_rate_combo.set_sensitive (false);
875 buffer_size_combo.set_sensitive (false);
876 input_latency.set_sensitive (false);
877 output_latency.set_sensitive (false);
878 input_channels.set_sensitive (false);
879 output_channels.set_sensitive (false);
881 ok_button->set_sensitive (false);
882 apply_button->set_sensitive (false);
884 ok_button->set_sensitive (true);
885 apply_button->set_sensitive (true);
886 if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
887 sample_rate_combo.set_sensitive (true);
889 if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
890 buffer_size_combo.set_sensitive (true);
898 EngineControl::driver_changed ()
900 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
903 backend->set_driver (driver_combo.get_active_text());
906 if (!ignore_changes) {
907 maybe_display_saved_state ();
912 EngineControl::device_changed ()
915 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
917 string device_name = device_combo.get_active_text ();
920 if (device_name != backend->device_name()) {
921 /* we set the backend-device to query various device related intormation.
922 * This has the side effect that backend->device_name() will match
923 * the device_name and 'change_device' will never be true.
924 * so work around this by setting...
926 queue_device_changed = true;
929 //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
930 backend->set_device_name(device_name);
933 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
935 /* don't allow programmatic change to combos to cause a
936 recursive call to this method.
946 sr = backend->available_sample_rates (device_name);
949 sr.push_back (8000.0f);
950 sr.push_back (16000.0f);
951 sr.push_back (32000.0f);
952 sr.push_back (44100.0f);
953 sr.push_back (48000.0f);
954 sr.push_back (88200.0f);
955 sr.push_back (96000.0f);
956 sr.push_back (192000.0f);
957 sr.push_back (384000.0f);
960 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
961 s.push_back (rate_as_string (*x));
962 if (*x == _desired_sample_rate) {
968 sample_rate_combo.set_sensitive (true);
969 set_popdown_strings (sample_rate_combo, s);
971 if (desired.empty()) {
972 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
974 sample_rate_combo.set_active_text (desired);
978 sample_rate_combo.set_sensitive (false);
986 bs = backend->available_buffer_sizes (device_name);
987 } else if (backend->can_change_buffer_size_when_running()) {
1001 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1002 s.push_back (bufsize_as_string (*x));
1006 buffer_size_combo.set_sensitive (true);
1007 set_popdown_strings (buffer_size_combo, s);
1009 uint32_t period = backend->buffer_size();
1011 period = backend->default_buffer_size(device_name);
1013 set_active_text_if_present (buffer_size_combo, bufsize_as_string (period));
1014 show_buffer_duration ();
1016 buffer_size_combo.set_sensitive (false);
1019 /* XXX theoretically need to set min + max channel counts here
1022 manage_control_app_sensitivity ();
1025 /* pick up any saved state for this device */
1027 if (!ignore_changes) {
1028 maybe_display_saved_state ();
1033 EngineControl::bufsize_as_string (uint32_t sz)
1035 /* Translators: "samples" is always plural here, so no
1036 need for plural+singular forms.
1039 snprintf (buf, sizeof (buf), _("%u samples"), sz);
1044 EngineControl::sample_rate_changed ()
1046 /* reset the strings for buffer size to show the correct msec value
1047 (reflecting the new sample rate).
1050 show_buffer_duration ();
1055 EngineControl::buffer_size_changed ()
1057 show_buffer_duration ();
1061 EngineControl::show_buffer_duration ()
1064 /* buffer sizes - convert from just samples to samples + msecs for
1065 * the displayed string
1068 string bs_text = buffer_size_combo.get_active_text ();
1069 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1070 uint32_t rate = get_rate();
1072 /* Developers: note the hard-coding of a double buffered model
1073 in the (2 * samples) computation of latency. we always start
1074 the audiobackend in this configuration.
1076 /* note to jack1 developers: ardour also always starts the engine
1077 * in async mode (no jack2 --sync option) which adds an extra cycle
1078 * of latency with jack2 (and *3 would be correct)
1079 * The value can also be wrong if jackd is started externally..
1081 * At the time of writing the ALSA backend always uses double-buffering *2,
1082 * The Dummy backend *1, and who knows what ASIO really does :)
1084 * So just display the period size, that's also what
1085 * ARDOUR_UI::update_sample_rate() does for the status bar.
1086 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1087 * but still, that's the buffer period, not [round-trip] latency)
1090 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1091 buffer_size_duration_label.set_text (buf);
1095 EngineControl::midi_option_changed ()
1097 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1100 backend->set_midi_option (get_midi_option());
1102 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1104 //_midi_devices.clear(); // TODO merge with state-saved settings..
1105 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1106 std::vector<MidiDeviceSettings> new_devices;
1108 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1109 MidiDeviceSettings mds = find_midi_device (i->name);
1110 if (i->available && !mds) {
1111 uint32_t input_latency = 0;
1112 uint32_t output_latency = 0;
1113 if (_can_set_midi_latencies) {
1114 input_latency = backend->systemic_midi_input_latency (i->name);
1115 output_latency = backend->systemic_midi_output_latency (i->name);
1117 bool enabled = backend->midi_device_enabled (i->name);
1118 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1119 new_devices.push_back (ptr);
1120 } else if (i->available) {
1121 new_devices.push_back (mds);
1124 _midi_devices = new_devices;
1126 if (_midi_devices.empty()) {
1127 midi_devices_button.set_sensitive (false);
1129 midi_devices_button.set_sensitive (true);
1134 EngineControl::parameter_changed ()
1138 EngineControl::State
1139 EngineControl::get_matching_state (
1140 const string& backend,
1141 const string& driver,
1142 const string& device)
1144 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1145 if ((*i)->backend == backend &&
1146 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1154 EngineControl::State
1155 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1157 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1160 return get_matching_state (backend_combo.get_active_text(),
1161 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1162 device_combo.get_active_text());
1166 return get_matching_state (backend_combo.get_active_text(),
1168 device_combo.get_active_text());
1171 EngineControl::State
1172 EngineControl::save_state ()
1176 if (!_have_control) {
1177 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1181 state.reset(new StateStruct);
1182 state->backend = get_backend ();
1184 state.reset(new StateStruct);
1185 store_state (state);
1188 for (StateList::iterator i = states.begin(); i != states.end();) {
1189 if ((*i)->backend == state->backend &&
1190 (*i)->driver == state->driver &&
1191 (*i)->device == state->device) {
1192 i = states.erase(i);
1198 states.push_back (state);
1204 EngineControl::store_state (State state)
1206 state->backend = get_backend ();
1207 state->driver = get_driver ();
1208 state->device = get_device_name ();
1209 state->sample_rate = get_rate ();
1210 state->buffer_size = get_buffer_size ();
1211 state->input_latency = get_input_latency ();
1212 state->output_latency = get_output_latency ();
1213 state->input_channels = get_input_channels ();
1214 state->output_channels = get_output_channels ();
1215 state->midi_option = get_midi_option ();
1216 state->midi_devices = _midi_devices;
1220 EngineControl::maybe_display_saved_state ()
1222 if (!_have_control) {
1226 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1229 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1231 if (!_desired_sample_rate) {
1232 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1234 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1235 /* call this explicitly because we're ignoring changes to
1236 the controls at this point.
1238 show_buffer_duration ();
1239 input_latency.set_value (state->input_latency);
1240 output_latency.set_value (state->output_latency);
1242 if (!state->midi_option.empty()) {
1243 midi_option_combo.set_active_text (state->midi_option);
1244 _midi_devices = state->midi_devices;
1250 EngineControl::get_state ()
1252 XMLNode* root = new XMLNode ("AudioMIDISetup");
1255 if (!states.empty()) {
1256 XMLNode* state_nodes = new XMLNode ("EngineStates");
1258 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1260 XMLNode* node = new XMLNode ("State");
1262 node->add_property ("backend", (*i)->backend);
1263 node->add_property ("driver", (*i)->driver);
1264 node->add_property ("device", (*i)->device);
1265 node->add_property ("sample-rate", (*i)->sample_rate);
1266 node->add_property ("buffer-size", (*i)->buffer_size);
1267 node->add_property ("input-latency", (*i)->input_latency);
1268 node->add_property ("output-latency", (*i)->output_latency);
1269 node->add_property ("input-channels", (*i)->input_channels);
1270 node->add_property ("output-channels", (*i)->output_channels);
1271 node->add_property ("active", (*i)->active ? "yes" : "no");
1272 node->add_property ("midi-option", (*i)->midi_option);
1274 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1275 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1276 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1277 midi_device_stuff->add_property (X_("name"), (*p)->name);
1278 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1279 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1280 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1281 midi_devices->add_child_nocopy (*midi_device_stuff);
1283 node->add_child_nocopy (*midi_devices);
1285 state_nodes->add_child_nocopy (*node);
1288 root->add_child_nocopy (*state_nodes);
1295 EngineControl::set_state (const XMLNode& root)
1297 XMLNodeList clist, cclist;
1298 XMLNodeConstIterator citer, cciter;
1300 XMLNode* grandchild;
1301 XMLProperty* prop = NULL;
1303 if (root.name() != "AudioMIDISetup") {
1307 clist = root.children();
1311 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1315 if (child->name() != "EngineStates") {
1319 cclist = child->children();
1321 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1322 State state (new StateStruct);
1324 grandchild = *cciter;
1326 if (grandchild->name() != "State") {
1330 if ((prop = grandchild->property ("backend")) == 0) {
1333 state->backend = prop->value ();
1335 if ((prop = grandchild->property ("driver")) == 0) {
1338 state->driver = prop->value ();
1340 if ((prop = grandchild->property ("device")) == 0) {
1343 state->device = prop->value ();
1345 if ((prop = grandchild->property ("sample-rate")) == 0) {
1348 state->sample_rate = atof (prop->value ());
1350 if ((prop = grandchild->property ("buffer-size")) == 0) {
1353 state->buffer_size = atoi (prop->value ());
1355 if ((prop = grandchild->property ("input-latency")) == 0) {
1358 state->input_latency = atoi (prop->value ());
1360 if ((prop = grandchild->property ("output-latency")) == 0) {
1363 state->output_latency = atoi (prop->value ());
1365 if ((prop = grandchild->property ("input-channels")) == 0) {
1368 state->input_channels = atoi (prop->value ());
1370 if ((prop = grandchild->property ("output-channels")) == 0) {
1373 state->output_channels = atoi (prop->value ());
1375 if ((prop = grandchild->property ("active")) == 0) {
1378 state->active = string_is_affirmative (prop->value ());
1380 if ((prop = grandchild->property ("midi-option")) == 0) {
1383 state->midi_option = prop->value ();
1385 state->midi_devices.clear();
1387 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1388 const XMLNodeList mnc = midinode->children();
1389 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1390 if ((*n)->property (X_("name")) == 0
1391 || (*n)->property (X_("enabled")) == 0
1392 || (*n)->property (X_("input-latency")) == 0
1393 || (*n)->property (X_("output-latency")) == 0
1398 MidiDeviceSettings ptr (new MidiDeviceSetting(
1399 (*n)->property (X_("name"))->value (),
1400 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1401 atoi ((*n)->property (X_("input-latency"))->value ()),
1402 atoi ((*n)->property (X_("output-latency"))->value ())
1404 state->midi_devices.push_back (ptr);
1409 /* remove accumulated duplicates (due to bug in ealier version)
1410 * this can be removed again before release
1412 for (StateList::iterator i = states.begin(); i != states.end();) {
1413 if ((*i)->backend == state->backend &&
1414 (*i)->driver == state->driver &&
1415 (*i)->device == state->device) {
1416 i = states.erase(i);
1423 states.push_back (state);
1427 /* now see if there was an active state and switch the setup to it */
1429 // purge states of backend that are not available in this built
1430 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1431 vector<std::string> backend_names;
1433 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1434 backend_names.push_back((*i)->name);
1436 for (StateList::iterator i = states.begin(); i != states.end();) {
1437 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1438 i = states.erase(i);
1444 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1447 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1448 backend_combo.set_active_text ((*i)->backend);
1449 driver_combo.set_active_text ((*i)->driver);
1450 device_combo.set_active_text ((*i)->device);
1451 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1452 set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
1453 input_latency.set_value ((*i)->input_latency);
1454 output_latency.set_value ((*i)->output_latency);
1455 midi_option_combo.set_active_text ((*i)->midi_option);
1462 EngineControl::push_state_to_backend (bool start)
1464 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1470 /* figure out what is going to change */
1472 bool restart_required = false;
1473 bool was_running = ARDOUR::AudioEngine::instance()->running();
1474 bool change_driver = false;
1475 bool change_device = false;
1476 bool change_rate = false;
1477 bool change_bufsize = false;
1478 bool change_latency = false;
1479 bool change_channels = false;
1480 bool change_midi = false;
1482 uint32_t ochan = get_output_channels ();
1483 uint32_t ichan = get_input_channels ();
1485 if (_have_control) {
1487 if (started_at_least_once) {
1489 /* we can control the backend */
1491 if (backend->requires_driver_selection()) {
1492 if (get_driver() != backend->driver_name()) {
1493 change_driver = true;
1497 if (queue_device_changed || get_device_name() != backend->device_name()) {
1498 change_device = true;
1501 if (get_rate() != backend->sample_rate()) {
1505 if (get_buffer_size() != backend->buffer_size()) {
1506 change_bufsize = true;
1509 if (get_midi_option() != backend->midi_option()) {
1513 /* zero-requested channels means "all available" */
1516 ichan = backend->input_channels();
1520 ochan = backend->output_channels();
1523 if (ichan != backend->input_channels()) {
1524 change_channels = true;
1527 if (ochan != backend->output_channels()) {
1528 change_channels = true;
1531 if (get_input_latency() != backend->systemic_input_latency() ||
1532 get_output_latency() != backend->systemic_output_latency()) {
1533 change_latency = true;
1536 /* backend never started, so we have to force a group
1539 change_device = true;
1540 if (backend->requires_driver_selection()) {
1541 change_driver = true;
1544 change_bufsize = true;
1545 change_channels = true;
1546 change_latency = true;
1552 /* we have no control over the backend, meaning that we can
1553 * only possibly change sample rate and buffer size.
1557 if (get_rate() != backend->sample_rate()) {
1558 change_bufsize = true;
1561 if (get_buffer_size() != backend->buffer_size()) {
1562 change_bufsize = true;
1566 queue_device_changed = false;
1568 if (!_have_control) {
1570 /* We do not have control over the backend, so the best we can
1571 * do is try to change the sample rate and/or bufsize and get
1575 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1579 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1584 backend->set_sample_rate (get_rate());
1587 if (change_bufsize) {
1588 backend->set_buffer_size (get_buffer_size());
1592 if (ARDOUR::AudioEngine::instance()->start ()) {
1593 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1603 /* determine if we need to stop the backend before changing parameters */
1605 if (change_driver || change_device || change_channels || change_latency ||
1606 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1608 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1609 restart_required = true;
1611 restart_required = false;
1616 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1617 /* no changes in any parameters that absolutely require a
1618 * restart, so check those that might be changeable without a
1622 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1623 /* can't do this while running ... */
1624 restart_required = true;
1627 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1628 /* can't do this while running ... */
1629 restart_required = true;
1635 if (restart_required) {
1636 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1643 if (change_driver && backend->set_driver (get_driver())) {
1644 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1647 if (change_device && backend->set_device_name (get_device_name())) {
1648 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1651 if (change_rate && backend->set_sample_rate (get_rate())) {
1652 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1655 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1656 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1660 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1661 if (backend->set_input_channels (get_input_channels())) {
1662 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1665 if (backend->set_output_channels (get_output_channels())) {
1666 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1670 if (change_latency) {
1671 if (backend->set_systemic_input_latency (get_input_latency())) {
1672 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1675 if (backend->set_systemic_output_latency (get_output_latency())) {
1676 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1682 backend->set_midi_option (get_midi_option());
1686 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1687 if (_measure_midi) {
1688 if (*p == _measure_midi) {
1689 backend->set_midi_device_enabled ((*p)->name, true);
1691 backend->set_midi_device_enabled ((*p)->name, false);
1695 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1696 if (backend->can_set_systemic_midi_latencies()) {
1697 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1698 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1703 if (start || (was_running && restart_required)) {
1704 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1715 EngineControl::post_push ()
1717 /* get a pointer to the current state object, creating one if
1721 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1724 state = save_state ();
1732 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1733 (*i)->active = false;
1736 /* mark this one active (to be used next time the dialog is
1740 state->active = true;
1742 if (_have_control) { // XXX
1743 manage_control_app_sensitivity ();
1746 /* schedule a redisplay of MIDI ports */
1747 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1752 EngineControl::get_rate () const
1754 float r = atof (sample_rate_combo.get_active_text ());
1755 /* the string may have been translated with an abbreviation for
1756 * thousands, so use a crude heuristic to fix this.
1766 EngineControl::get_buffer_size () const
1768 string txt = buffer_size_combo.get_active_text ();
1771 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1772 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
1773 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
1781 EngineControl::get_midi_option () const
1783 return midi_option_combo.get_active_text();
1787 EngineControl::get_input_channels() const
1789 if (ARDOUR::Profile->get_mixbus()) {
1790 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1791 if (!backend) return 0;
1792 return backend->input_channels();
1794 return (uint32_t) input_channels_adjustment.get_value();
1798 EngineControl::get_output_channels() const
1800 if (ARDOUR::Profile->get_mixbus()) {
1801 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1802 if (!backend) return 0;
1803 return backend->input_channels();
1805 return (uint32_t) output_channels_adjustment.get_value();
1809 EngineControl::get_input_latency() const
1811 return (uint32_t) input_latency_adjustment.get_value();
1815 EngineControl::get_output_latency() const
1817 return (uint32_t) output_latency_adjustment.get_value();
1821 EngineControl::get_backend () const
1823 return backend_combo.get_active_text ();
1827 EngineControl::get_driver () const
1829 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1830 return driver_combo.get_active_text ();
1837 EngineControl::get_device_name () const
1839 return device_combo.get_active_text ();
1843 EngineControl::control_app_button_clicked ()
1845 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1851 backend->launch_control_app ();
1855 EngineControl::manage_control_app_sensitivity ()
1857 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1863 string appname = backend->control_app_name();
1865 if (appname.empty()) {
1866 control_app_button.set_sensitive (false);
1868 control_app_button.set_sensitive (true);
1873 EngineControl::set_desired_sample_rate (uint32_t sr)
1875 _desired_sample_rate = sr;
1880 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1882 if (page_num == 0) {
1883 cancel_button->set_sensitive (true);
1884 ok_button->set_sensitive (true);
1885 apply_button->set_sensitive (true);
1886 _measure_midi.reset();
1888 cancel_button->set_sensitive (false);
1889 ok_button->set_sensitive (false);
1890 apply_button->set_sensitive (false);
1893 if (page_num == midi_tab) {
1895 refresh_midi_display ();
1898 if (page_num == latency_tab) {
1901 if (ARDOUR::AudioEngine::instance()->running()) {
1902 // TODO - mark as 'stopped for latency
1903 ARDOUR_UI::instance()->disconnect_from_engine ();
1907 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1909 /* save any existing latency values */
1911 uint32_t il = (uint32_t) input_latency.get_value ();
1912 uint32_t ol = (uint32_t) input_latency.get_value ();
1914 /* reset to zero so that our new test instance
1915 will be clean of any existing latency measures.
1917 NB. this should really be done by the backend
1918 when stated for latency measurement.
1921 input_latency.set_value (0);
1922 output_latency.set_value (0);
1924 push_state_to_backend (false);
1928 input_latency.set_value (il);
1929 output_latency.set_value (ol);
1932 // This should be done in push_state_to_backend()
1933 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1934 disable_latency_tab ();
1937 enable_latency_tab ();
1941 end_latency_detection ();
1942 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1947 /* latency measurement */
1950 EngineControl::check_audio_latency_measurement ()
1952 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1954 if (mtdm->resolve () < 0) {
1955 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1959 if (mtdm->err () > 0.3) {
1965 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1967 if (sample_rate == 0) {
1968 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1969 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1973 int frames_total = mtdm->del();
1974 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1976 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1977 _("Detected roundtrip latency: "),
1978 frames_total, frames_total * 1000.0f/sample_rate,
1979 _("Systemic latency: "),
1980 extra, extra * 1000.0f/sample_rate);
1984 if (mtdm->err () > 0.2) {
1986 strcat (buf, _("(signal detection error)"));
1992 strcat (buf, _("(inverted - bad wiring)"));
1996 lm_results.set_markup (string_compose (results_markup, buf));
1999 have_lm_results = true;
2000 end_latency_detection ();
2001 lm_use_button.set_sensitive (true);
2009 EngineControl::check_midi_latency_measurement ()
2011 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2013 if (!mididm->have_signal () || mididm->latency () == 0) {
2014 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2019 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2021 if (sample_rate == 0) {
2022 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2023 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2027 ARDOUR::framecnt_t frames_total = mididm->latency();
2028 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2029 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2030 _("Detected roundtrip latency: "),
2031 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2032 _("Systemic latency: "),
2033 extra, extra * 1000.0f / sample_rate);
2037 if (!mididm->ok ()) {
2039 strcat (buf, _("(averaging)"));
2043 if (mididm->deviation () > 50.0) {
2045 strcat (buf, _("(too large jitter)"));
2047 } else if (mididm->deviation () > 10.0) {
2049 strcat (buf, _("(large jitter)"));
2053 have_lm_results = true;
2054 end_latency_detection ();
2055 lm_use_button.set_sensitive (true);
2056 lm_results.set_markup (string_compose (results_markup, buf));
2058 } else if (mididm->processed () > 400) {
2059 have_lm_results = false;
2060 end_latency_detection ();
2061 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2065 lm_results.set_markup (string_compose (results_markup, buf));
2071 EngineControl::start_latency_detection ()
2073 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2074 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2076 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2077 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2078 if (_measure_midi) {
2079 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2081 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2083 lm_measure_label.set_text (_("Cancel"));
2084 have_lm_results = false;
2085 lm_use_button.set_sensitive (false);
2086 lm_input_channel_combo.set_sensitive (false);
2087 lm_output_channel_combo.set_sensitive (false);
2093 EngineControl::end_latency_detection ()
2095 latency_timeout.disconnect ();
2096 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2097 lm_measure_label.set_text (_("Measure"));
2098 if (!have_lm_results) {
2099 lm_use_button.set_sensitive (false);
2101 lm_input_channel_combo.set_sensitive (true);
2102 lm_output_channel_combo.set_sensitive (true);
2107 EngineControl::latency_button_clicked ()
2110 start_latency_detection ();
2112 end_latency_detection ();
2117 EngineControl::use_latency_button_clicked ()
2119 if (_measure_midi) {
2120 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2124 ARDOUR::framecnt_t frames_total = mididm->latency();
2125 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2126 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2127 _measure_midi->input_latency = one_way;
2128 _measure_midi->output_latency = one_way;
2129 notebook.set_current_page (midi_tab);
2131 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2137 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2138 one_way = std::max (0., one_way);
2140 input_latency_adjustment.set_value (one_way);
2141 output_latency_adjustment.set_value (one_way);
2143 /* back to settings page */
2144 notebook.set_current_page (0);
2150 EngineControl::on_delete_event (GdkEventAny* ev)
2152 if (notebook.get_current_page() == 2) {
2153 /* currently on latency tab - be sure to clean up */
2154 end_latency_detection ();
2156 return ArdourDialog::on_delete_event (ev);
2160 EngineControl::engine_running ()
2162 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2165 set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2166 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2168 buffer_size_combo.set_sensitive (true);
2169 sample_rate_combo.set_sensitive (true);
2171 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2172 connect_disconnect_button.show();
2174 started_at_least_once = true;
2175 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2179 EngineControl::engine_stopped ()
2181 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2184 buffer_size_combo.set_sensitive (false);
2185 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2186 connect_disconnect_button.show();
2188 sample_rate_combo.set_sensitive (true);
2189 buffer_size_combo.set_sensitive (true);
2190 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2194 EngineControl::device_list_changed ()
2196 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1); // ??
2198 midi_option_changed();
2202 EngineControl::connect_disconnect_click()
2204 if (ARDOUR::AudioEngine::instance()->running()) {
2205 ARDOUR_UI::instance()->disconnect_from_engine ();
2207 ARDOUR_UI::instance()->reconnect_to_engine ();
2212 EngineControl::calibrate_audio_latency ()
2214 _measure_midi.reset ();
2215 have_lm_results = false;
2216 lm_use_button.set_sensitive (false);
2217 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2218 notebook.set_current_page (latency_tab);
2222 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2225 have_lm_results = false;
2226 lm_use_button.set_sensitive (false);
2227 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2228 notebook.set_current_page (latency_tab);
2232 EngineControl::configure_midi_devices ()
2234 notebook.set_current_page (midi_tab);