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 backend_combo.set_active_text (backend_names.front());
292 /* ignore: don't save state */
293 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
297 /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
298 if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
299 backend_combo.set_active_text (backend_names.back());
300 /* ignore: don't save state */
301 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
306 /* Connect to signals */
308 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
309 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
310 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
311 device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
312 midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
314 input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
315 output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
316 input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
317 output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
319 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
321 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
322 connect_disconnect_button.set_no_show_all();
327 EngineControl::on_show ()
329 ArdourDialog::on_show ();
331 ok_button->grab_focus();
335 EngineControl::on_response (int response_id)
337 ArdourDialog::on_response (response_id);
339 switch (response_id) {
341 push_state_to_backend (true);
344 push_state_to_backend (true);
347 case RESPONSE_DELETE_EVENT:
350 ev.type = GDK_BUTTON_PRESS;
352 on_delete_event ((GdkEventAny*) &ev);
361 EngineControl::build_notebook ()
364 AttachOptions xopt = AttachOptions (FILL|EXPAND);
366 /* clear the table */
368 Gtkmm2ext::container_clear (basic_vbox);
369 Gtkmm2ext::container_clear (basic_packer);
371 if (control_app_button.get_parent()) {
372 control_app_button.get_parent()->remove (control_app_button);
375 label = manage (left_aligned_label (_("Audio System:")));
376 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
377 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
379 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
380 lm_button_audio.set_name ("generic button");
381 lm_button_audio.set_can_focus(true);
384 build_full_control_notebook ();
386 build_no_control_notebook ();
389 basic_vbox.pack_start (basic_hbox, false, false);
392 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
393 basic_vbox.show_all ();
398 EngineControl::build_full_control_notebook ()
400 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
403 using namespace Notebook_Helpers;
405 vector<string> strings;
406 AttachOptions xopt = AttachOptions (FILL|EXPAND);
407 int row = 1; // row zero == backend combo
409 /* start packing it up */
411 if (backend->requires_driver_selection()) {
412 label = manage (left_aligned_label (_("Driver:")));
413 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
414 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
418 label = manage (left_aligned_label (_("Device:")));
419 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
420 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
423 label = manage (left_aligned_label (_("Sample rate:")));
424 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
425 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
429 label = manage (left_aligned_label (_("Buffer size:")));
430 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
431 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
432 buffer_size_duration_label.set_alignment (0.0); /* left-align */
433 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
435 /* button spans 2 rows */
437 basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
440 input_channels.set_name ("InputChannels");
441 input_channels.set_flags (Gtk::CAN_FOCUS);
442 input_channels.set_digits (0);
443 input_channels.set_wrap (false);
444 output_channels.set_editable (true);
446 if (!ARDOUR::Profile->get_mixbus()) {
447 label = manage (left_aligned_label (_("Input Channels:")));
448 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
449 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
453 output_channels.set_name ("OutputChannels");
454 output_channels.set_flags (Gtk::CAN_FOCUS);
455 output_channels.set_digits (0);
456 output_channels.set_wrap (false);
457 output_channels.set_editable (true);
459 if (!ARDOUR::Profile->get_mixbus()) {
460 label = manage (left_aligned_label (_("Output Channels:")));
461 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
462 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
466 input_latency.set_name ("InputLatency");
467 input_latency.set_flags (Gtk::CAN_FOCUS);
468 input_latency.set_digits (0);
469 input_latency.set_wrap (false);
470 input_latency.set_editable (true);
472 label = manage (left_aligned_label (_("Hardware input latency:")));
473 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
474 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
475 label = manage (left_aligned_label (_("samples")));
476 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
479 output_latency.set_name ("OutputLatency");
480 output_latency.set_flags (Gtk::CAN_FOCUS);
481 output_latency.set_digits (0);
482 output_latency.set_wrap (false);
483 output_latency.set_editable (true);
485 label = manage (left_aligned_label (_("Hardware output latency:")));
486 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
487 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
488 label = manage (left_aligned_label (_("samples")));
489 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
491 /* button spans 2 rows */
493 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
496 label = manage (left_aligned_label (_("MIDI System")));
497 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
498 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
499 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
504 EngineControl::build_no_control_notebook ()
506 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
509 using namespace Notebook_Helpers;
511 vector<string> strings;
512 AttachOptions xopt = AttachOptions (FILL|EXPAND);
513 int row = 1; // row zero == backend combo
514 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
516 label = manage (new Label);
517 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
518 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
521 if (backend->can_change_sample_rate_when_running()) {
522 label = manage (left_aligned_label (_("Sample rate:")));
523 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
524 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
528 if (backend->can_change_buffer_size_when_running()) {
529 label = manage (left_aligned_label (_("Buffer size:")));
530 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
531 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
532 buffer_size_duration_label.set_alignment (0.0); /* left-align */
533 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
537 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
541 EngineControl::~EngineControl ()
543 ignore_changes = true;
547 EngineControl::disable_latency_tab ()
549 vector<string> empty;
550 set_popdown_strings (lm_output_channel_combo, empty);
551 set_popdown_strings (lm_input_channel_combo, empty);
552 lm_measure_button.set_sensitive (false);
553 lm_use_button.set_sensitive (false);
557 EngineControl::enable_latency_tab ()
559 vector<string> outputs;
560 vector<string> inputs;
562 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
563 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
564 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
566 if (!ARDOUR::AudioEngine::instance()->running()) {
567 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
568 notebook.set_current_page (0);
572 else if (inputs.empty() || outputs.empty()) {
573 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
574 notebook.set_current_page (0);
579 lm_back_button_signal.disconnect();
581 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
584 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
588 set_popdown_strings (lm_output_channel_combo, outputs);
589 lm_output_channel_combo.set_active_text (outputs.front());
590 lm_output_channel_combo.set_sensitive (true);
592 set_popdown_strings (lm_input_channel_combo, inputs);
593 lm_input_channel_combo.set_active_text (inputs.front());
594 lm_input_channel_combo.set_sensitive (true);
596 lm_measure_button.set_sensitive (true);
600 EngineControl::setup_midi_tab_for_backend ()
602 string backend = backend_combo.get_active_text ();
604 Gtkmm2ext::container_clear (midi_vbox);
606 midi_vbox.set_border_width (12);
607 midi_device_table.set_border_width (12);
609 if (backend == "JACK") {
610 setup_midi_tab_for_jack ();
613 midi_vbox.pack_start (midi_device_table, true, true);
614 midi_vbox.pack_start (midi_back_button, false, false);
615 midi_vbox.show_all ();
619 EngineControl::setup_midi_tab_for_jack ()
624 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
626 device->input_latency = a->get_value();
628 device->output_latency = a->get_value();
633 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
634 b->set_active (!b->get_active());
635 device->enabled = b->get_active();
636 refresh_midi_display(device->name);
640 EngineControl::refresh_midi_display (std::string focus)
642 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
646 AttachOptions xopt = AttachOptions (FILL|EXPAND);
649 Gtkmm2ext::container_clear (midi_device_table);
651 midi_device_table.set_spacings (6);
653 l = manage (new Label);
654 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
655 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
656 l->set_alignment (0.5, 0.5);
660 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
661 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
662 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
663 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
665 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
666 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
667 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
668 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
671 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
676 bool enabled = (*p)->enabled;
678 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
679 m->set_name ("midi device");
680 m->set_can_focus (Gtk::CAN_FOCUS);
681 m->add_events (Gdk::BUTTON_RELEASE_MASK);
682 m->set_active (enabled);
683 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
684 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
685 if ((*p)->name == focus) {
689 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
690 s = manage (new Gtk::SpinButton (*a));
691 a->set_value ((*p)->input_latency);
692 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
693 s->set_sensitive (_can_set_midi_latencies && enabled);
694 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
696 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
697 s = manage (new Gtk::SpinButton (*a));
698 a->set_value ((*p)->output_latency);
699 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
700 s->set_sensitive (_can_set_midi_latencies && enabled);
701 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
703 b = manage (new Button (_("Calibrate")));
704 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
705 b->set_sensitive (_can_set_midi_latencies && enabled);
706 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
713 EngineControl::update_sensitivity ()
718 EngineControl::backend_changed ()
720 string backend_name = backend_combo.get_active_text();
721 boost::shared_ptr<ARDOUR::AudioBackend> backend;
723 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
724 /* eh? setting the backend failed... how ? */
725 /* A: stale config contains a backend that does not exist in current build */
729 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
732 setup_midi_tab_for_backend ();
733 _midi_devices.clear();
735 if (backend->requires_driver_selection()) {
736 vector<string> drivers = backend->enumerate_drivers();
737 driver_combo.set_sensitive (true);
739 if (!drivers.empty()) {
741 string current_driver;
742 current_driver = backend->driver_name ();
744 // driver might not have been set yet
745 if (current_driver == "") {
746 current_driver = driver_combo.get_active_text ();
747 if (current_driver == "")
748 // driver has never been set, make sure it's not blank
749 current_driver = drivers.front ();
752 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
753 set_popdown_strings (driver_combo, drivers);
754 driver_combo.set_active_text (current_driver);
761 driver_combo.set_sensitive (false);
762 /* this will change the device text which will cause a call to
763 * device changed which will set up parameters
768 vector<string> midi_options = backend->enumerate_midi_options();
770 if (midi_options.size() == 1) {
771 /* only contains the "none" option */
772 midi_option_combo.set_sensitive (false);
775 set_popdown_strings (midi_option_combo, midi_options);
776 midi_option_combo.set_active_text (midi_options.front());
777 midi_option_combo.set_sensitive (true);
779 midi_option_combo.set_sensitive (false);
783 connect_disconnect_button.hide();
785 midi_option_changed();
787 started_at_least_once = false;
789 if (!ignore_changes) {
790 maybe_display_saved_state ();
795 EngineControl::print_channel_count (Gtk::SpinButton* sb)
797 if (ARDOUR::Profile->get_mixbus()) {
798 cout << "Mixbus crash trap. sb->get_value(): " << sb->get_value();
802 uint32_t cnt = (uint32_t) sb->get_value();
804 sb->set_text (_("all available channels"));
807 snprintf (buf, sizeof (buf), "%d", cnt);
814 EngineControl::list_devices ()
816 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
819 /* now fill out devices, mark sample rates, buffer sizes insensitive */
821 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
823 /* NOTE: Ardour currently does not display the "available" field of the
826 * Doing so would require a different GUI widget than the combo
827 * box/popdown that we currently use, since it has no way to list
828 * items that are not selectable. Something more like a popup menu,
829 * which could have unselectable items, would be appropriate.
832 vector<string> available_devices;
834 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
835 available_devices.push_back (i->name);
838 if (!available_devices.empty()) {
840 update_sensitivity ();
843 string current_device, found_device;
844 current_device = device_combo.get_active_text ();
845 if (current_device == "") {
846 current_device = backend->device_name ();
849 // Make sure that the active text is still relevant for this
850 // device (it might only be relevant to the previous device!!)
851 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
852 if (*i == current_device)
853 found_device = current_device;
855 if (found_device == "")
856 // device has never been set (or was not relevant
857 // for this backend) Let's make sure it's not blank
858 current_device = available_devices.front ();
860 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
861 set_popdown_strings (device_combo, available_devices);
863 device_combo.set_active_text (current_device);
868 input_latency.set_sensitive (true);
869 output_latency.set_sensitive (true);
870 input_channels.set_sensitive (true);
871 output_channels.set_sensitive (true);
873 ok_button->set_sensitive (true);
874 apply_button->set_sensitive (true);
877 device_combo.clear();
878 sample_rate_combo.set_sensitive (false);
879 buffer_size_combo.set_sensitive (false);
880 input_latency.set_sensitive (false);
881 output_latency.set_sensitive (false);
882 input_channels.set_sensitive (false);
883 output_channels.set_sensitive (false);
885 ok_button->set_sensitive (false);
886 apply_button->set_sensitive (false);
888 ok_button->set_sensitive (true);
889 apply_button->set_sensitive (true);
890 if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
891 sample_rate_combo.set_sensitive (true);
893 if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
894 buffer_size_combo.set_sensitive (true);
902 EngineControl::driver_changed ()
904 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
907 backend->set_driver (driver_combo.get_active_text());
910 if (!ignore_changes) {
911 maybe_display_saved_state ();
916 EngineControl::device_changed ()
919 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
921 string device_name = device_combo.get_active_text ();
924 if (device_name != backend->device_name()) {
925 /* we set the backend-device to query various device related intormation.
926 * This has the side effect that backend->device_name() will match
927 * the device_name and 'change_device' will never be true.
928 * so work around this by setting...
930 queue_device_changed = true;
933 //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
934 backend->set_device_name(device_name);
937 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
939 /* don't allow programmatic change to combos to cause a
940 recursive call to this method.
950 sr = backend->available_sample_rates (device_name);
953 sr.push_back (8000.0f);
954 sr.push_back (16000.0f);
955 sr.push_back (32000.0f);
956 sr.push_back (44100.0f);
957 sr.push_back (48000.0f);
958 sr.push_back (88200.0f);
959 sr.push_back (96000.0f);
960 sr.push_back (192000.0f);
961 sr.push_back (384000.0f);
964 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
965 s.push_back (rate_as_string (*x));
966 if (*x == _desired_sample_rate) {
972 sample_rate_combo.set_sensitive (true);
973 set_popdown_strings (sample_rate_combo, s);
975 if (desired.empty()) {
976 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
978 sample_rate_combo.set_active_text (desired);
982 sample_rate_combo.set_sensitive (false);
990 bs = backend->available_buffer_sizes (device_name);
991 } else if (backend->can_change_buffer_size_when_running()) {
1000 bs.push_back (2048);
1001 bs.push_back (4096);
1002 bs.push_back (8192);
1005 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1006 s.push_back (bufsize_as_string (*x));
1010 buffer_size_combo.set_sensitive (true);
1011 set_popdown_strings (buffer_size_combo, s);
1013 uint32_t period = backend->buffer_size();
1015 period = backend->default_buffer_size(device_name);
1017 buffer_size_combo.set_active_text (bufsize_as_string (period));
1018 show_buffer_duration ();
1020 buffer_size_combo.set_sensitive (false);
1023 /* XXX theoretically need to set min + max channel counts here
1026 manage_control_app_sensitivity ();
1029 /* pick up any saved state for this device */
1031 if (!ignore_changes) {
1032 maybe_display_saved_state ();
1037 EngineControl::bufsize_as_string (uint32_t sz)
1039 /* Translators: "samples" is always plural here, so no
1040 need for plural+singular forms.
1043 snprintf (buf, sizeof (buf), _("%u samples"), sz);
1048 EngineControl::sample_rate_changed ()
1050 /* reset the strings for buffer size to show the correct msec value
1051 (reflecting the new sample rate).
1054 show_buffer_duration ();
1055 if (!ignore_changes) {
1062 EngineControl::buffer_size_changed ()
1064 show_buffer_duration ();
1065 if (!ignore_changes) {
1071 EngineControl::show_buffer_duration ()
1074 /* buffer sizes - convert from just samples to samples + msecs for
1075 * the displayed string
1078 string bs_text = buffer_size_combo.get_active_text ();
1079 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1080 uint32_t rate = get_rate();
1082 /* Developers: note the hard-coding of a double buffered model
1083 in the (2 * samples) computation of latency. we always start
1084 the audiobackend in this configuration.
1086 /* note to jack1 developers: ardour also always starts the engine
1087 * in async mode (no jack2 --sync option) which adds an extra cycle
1088 * of latency with jack2 (and *3 would be correct)
1089 * The value can also be wrong if jackd is started externally..
1091 * At the time of writing the ALSA backend always uses double-buffering *2,
1092 * The Dummy backend *1, and who knows what ASIO really does :)
1094 * So just display the period size, that's also what
1095 * ARDOUR_UI::update_sample_rate() does for the status bar.
1096 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1097 * but still, that's the buffer period, not [round-trip] latency)
1100 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1101 buffer_size_duration_label.set_text (buf);
1105 EngineControl::midi_option_changed ()
1107 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1110 backend->set_midi_option (get_midi_option());
1112 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1114 //_midi_devices.clear(); // TODO merge with state-saved settings..
1115 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1116 std::vector<MidiDeviceSettings> new_devices;
1118 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1119 MidiDeviceSettings mds = find_midi_device (i->name);
1120 if (i->available && !mds) {
1121 uint32_t input_latency = 0;
1122 uint32_t output_latency = 0;
1123 if (_can_set_midi_latencies) {
1124 input_latency = backend->systemic_midi_input_latency (i->name);
1125 output_latency = backend->systemic_midi_output_latency (i->name);
1127 bool enabled = backend->midi_device_enabled (i->name);
1128 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1129 new_devices.push_back (ptr);
1130 } else if (i->available) {
1131 new_devices.push_back (mds);
1134 _midi_devices = new_devices;
1136 if (_midi_devices.empty()) {
1137 midi_devices_button.set_sensitive (false);
1139 midi_devices_button.set_sensitive (true);
1142 if (!ignore_changes) {
1148 EngineControl::parameter_changed ()
1150 if (!ignore_changes) {
1155 EngineControl::State
1156 EngineControl::get_matching_state (
1157 const string& backend,
1158 const string& driver,
1159 const string& device)
1161 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1162 if ((*i)->backend == backend &&
1163 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1171 EngineControl::State
1172 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1174 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1177 return get_matching_state (backend_combo.get_active_text(),
1178 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1179 device_combo.get_active_text());
1183 return get_matching_state (backend_combo.get_active_text(),
1185 device_combo.get_active_text());
1188 EngineControl::State
1189 EngineControl::save_state ()
1193 if (!_have_control) {
1194 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1198 state.reset(new StateStruct);
1199 state->backend = get_backend ();
1201 state.reset(new StateStruct);
1202 store_state (state);
1205 for (StateList::iterator i = states.begin(); i != states.end();) {
1206 if ((*i)->backend == state->backend &&
1207 (*i)->driver == state->driver &&
1208 (*i)->device == state->device) {
1209 i = states.erase(i);
1215 states.push_back (state);
1221 EngineControl::store_state (State state)
1223 state->backend = get_backend ();
1224 state->driver = get_driver ();
1225 state->device = get_device_name ();
1226 state->sample_rate = get_rate ();
1227 state->buffer_size = get_buffer_size ();
1228 state->input_latency = get_input_latency ();
1229 state->output_latency = get_output_latency ();
1230 state->input_channels = get_input_channels ();
1231 state->output_channels = get_output_channels ();
1232 state->midi_option = get_midi_option ();
1233 state->midi_devices = _midi_devices;
1237 EngineControl::maybe_display_saved_state ()
1239 if (!_have_control) {
1243 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1246 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1248 if (!_desired_sample_rate) {
1249 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1251 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
1252 /* call this explicitly because we're ignoring changes to
1253 the controls at this point.
1255 show_buffer_duration ();
1256 input_latency.set_value (state->input_latency);
1257 output_latency.set_value (state->output_latency);
1259 if (!state->midi_option.empty()) {
1260 midi_option_combo.set_active_text (state->midi_option);
1261 _midi_devices = state->midi_devices;
1267 EngineControl::get_state ()
1269 XMLNode* root = new XMLNode ("AudioMIDISetup");
1272 if (!states.empty()) {
1273 XMLNode* state_nodes = new XMLNode ("EngineStates");
1275 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1277 XMLNode* node = new XMLNode ("State");
1279 node->add_property ("backend", (*i)->backend);
1280 node->add_property ("driver", (*i)->driver);
1281 node->add_property ("device", (*i)->device);
1282 node->add_property ("sample-rate", (*i)->sample_rate);
1283 node->add_property ("buffer-size", (*i)->buffer_size);
1284 node->add_property ("input-latency", (*i)->input_latency);
1285 node->add_property ("output-latency", (*i)->output_latency);
1286 node->add_property ("input-channels", (*i)->input_channels);
1287 node->add_property ("output-channels", (*i)->output_channels);
1288 node->add_property ("active", (*i)->active ? "yes" : "no");
1289 node->add_property ("midi-option", (*i)->midi_option);
1291 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1292 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1293 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1294 midi_device_stuff->add_property (X_("name"), (*p)->name);
1295 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1296 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1297 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1298 midi_devices->add_child_nocopy (*midi_device_stuff);
1300 node->add_child_nocopy (*midi_devices);
1302 state_nodes->add_child_nocopy (*node);
1305 root->add_child_nocopy (*state_nodes);
1312 EngineControl::set_state (const XMLNode& root)
1314 XMLNodeList clist, cclist;
1315 XMLNodeConstIterator citer, cciter;
1317 XMLNode* grandchild;
1318 XMLProperty* prop = NULL;
1320 if (root.name() != "AudioMIDISetup") {
1324 clist = root.children();
1328 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1332 if (child->name() != "EngineStates") {
1336 cclist = child->children();
1338 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1339 State state (new StateStruct);
1341 grandchild = *cciter;
1343 if (grandchild->name() != "State") {
1347 if ((prop = grandchild->property ("backend")) == 0) {
1350 state->backend = prop->value ();
1352 if ((prop = grandchild->property ("driver")) == 0) {
1355 state->driver = prop->value ();
1357 if ((prop = grandchild->property ("device")) == 0) {
1360 state->device = prop->value ();
1362 if ((prop = grandchild->property ("sample-rate")) == 0) {
1365 state->sample_rate = atof (prop->value ());
1367 if ((prop = grandchild->property ("buffer-size")) == 0) {
1370 state->buffer_size = atoi (prop->value ());
1372 if ((prop = grandchild->property ("input-latency")) == 0) {
1375 state->input_latency = atoi (prop->value ());
1377 if ((prop = grandchild->property ("output-latency")) == 0) {
1380 state->output_latency = atoi (prop->value ());
1382 if ((prop = grandchild->property ("input-channels")) == 0) {
1385 state->input_channels = atoi (prop->value ());
1387 if ((prop = grandchild->property ("output-channels")) == 0) {
1390 state->output_channels = atoi (prop->value ());
1392 if ((prop = grandchild->property ("active")) == 0) {
1395 state->active = string_is_affirmative (prop->value ());
1397 if ((prop = grandchild->property ("midi-option")) == 0) {
1400 state->midi_option = prop->value ();
1402 state->midi_devices.clear();
1404 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1405 const XMLNodeList mnc = midinode->children();
1406 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1407 if ((*n)->property (X_("name")) == 0
1408 || (*n)->property (X_("enabled")) == 0
1409 || (*n)->property (X_("input-latency")) == 0
1410 || (*n)->property (X_("output-latency")) == 0
1415 MidiDeviceSettings ptr (new MidiDeviceSetting(
1416 (*n)->property (X_("name"))->value (),
1417 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1418 atoi ((*n)->property (X_("input-latency"))->value ()),
1419 atoi ((*n)->property (X_("output-latency"))->value ())
1421 state->midi_devices.push_back (ptr);
1426 /* remove accumulated duplicates (due to bug in ealier version)
1427 * this can be removed again before release
1429 for (StateList::iterator i = states.begin(); i != states.end();) {
1430 if ((*i)->backend == state->backend &&
1431 (*i)->driver == state->driver &&
1432 (*i)->device == state->device) {
1433 i = states.erase(i);
1440 states.push_back (state);
1444 /* now see if there was an active state and switch the setup to it */
1446 // purge states of backend that are not available in this built
1447 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1448 vector<std::string> backend_names;
1450 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1451 backend_names.push_back((*i)->name);
1453 for (StateList::iterator i = states.begin(); i != states.end();) {
1454 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1455 i = states.erase(i);
1461 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1465 backend_combo.set_active_text ((*i)->backend);
1466 driver_combo.set_active_text ((*i)->driver);
1467 device_combo.set_active_text ((*i)->device);
1468 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1469 buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
1470 input_latency.set_value ((*i)->input_latency);
1471 output_latency.set_value ((*i)->output_latency);
1472 midi_option_combo.set_active_text ((*i)->midi_option);
1480 EngineControl::push_state_to_backend (bool start)
1482 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1488 /* figure out what is going to change */
1490 bool restart_required = false;
1491 bool was_running = ARDOUR::AudioEngine::instance()->running();
1492 bool change_driver = false;
1493 bool change_device = false;
1494 bool change_rate = false;
1495 bool change_bufsize = false;
1496 bool change_latency = false;
1497 bool change_channels = false;
1498 bool change_midi = false;
1500 uint32_t ochan = get_output_channels ();
1501 uint32_t ichan = get_input_channels ();
1503 if (_have_control) {
1505 if (started_at_least_once) {
1507 /* we can control the backend */
1509 if (backend->requires_driver_selection()) {
1510 if (get_driver() != backend->driver_name()) {
1511 change_driver = true;
1515 if (queue_device_changed || get_device_name() != backend->device_name()) {
1516 change_device = true;
1519 if (get_rate() != backend->sample_rate()) {
1523 if (get_buffer_size() != backend->buffer_size()) {
1524 change_bufsize = true;
1527 if (get_midi_option() != backend->midi_option()) {
1531 /* zero-requested channels means "all available" */
1534 ichan = backend->input_channels();
1538 ochan = backend->output_channels();
1541 if (ichan != backend->input_channels()) {
1542 change_channels = true;
1545 if (ochan != backend->output_channels()) {
1546 change_channels = true;
1549 if (get_input_latency() != backend->systemic_input_latency() ||
1550 get_output_latency() != backend->systemic_output_latency()) {
1551 change_latency = true;
1554 /* backend never started, so we have to force a group
1557 change_device = true;
1558 if (backend->requires_driver_selection()) {
1559 change_driver = true;
1562 change_bufsize = true;
1563 change_channels = true;
1564 change_latency = true;
1570 /* we have no control over the backend, meaning that we can
1571 * only possibly change sample rate and buffer size.
1575 if (get_rate() != backend->sample_rate()) {
1576 change_bufsize = true;
1579 if (get_buffer_size() != backend->buffer_size()) {
1580 change_bufsize = true;
1584 queue_device_changed = false;
1586 if (!_have_control) {
1588 /* We do not have control over the backend, so the best we can
1589 * do is try to change the sample rate and/or bufsize and get
1593 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1597 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1602 backend->set_sample_rate (get_rate());
1605 if (change_bufsize) {
1606 backend->set_buffer_size (get_buffer_size());
1610 if (ARDOUR::AudioEngine::instance()->start ()) {
1611 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1621 /* determine if we need to stop the backend before changing parameters */
1623 if (change_driver || change_device || change_channels || change_latency ||
1624 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1626 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1627 restart_required = true;
1629 restart_required = false;
1634 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1635 /* no changes in any parameters that absolutely require a
1636 * restart, so check those that might be changeable without a
1640 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1641 /* can't do this while running ... */
1642 restart_required = true;
1645 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1646 /* can't do this while running ... */
1647 restart_required = true;
1653 if (restart_required) {
1654 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1661 if (change_driver && backend->set_driver (get_driver())) {
1662 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1665 if (change_device && backend->set_device_name (get_device_name())) {
1666 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1669 if (change_rate && backend->set_sample_rate (get_rate())) {
1670 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1673 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1674 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1678 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1679 if (backend->set_input_channels (get_input_channels())) {
1680 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1683 if (backend->set_output_channels (get_output_channels())) {
1684 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1688 if (change_latency) {
1689 if (backend->set_systemic_input_latency (get_input_latency())) {
1690 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1693 if (backend->set_systemic_output_latency (get_output_latency())) {
1694 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1700 backend->set_midi_option (get_midi_option());
1704 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1705 if (_measure_midi) {
1706 if (*p == _measure_midi) {
1707 backend->set_midi_device_enabled ((*p)->name, true);
1709 backend->set_midi_device_enabled ((*p)->name, false);
1713 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1714 if (backend->can_set_systemic_midi_latencies()) {
1715 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1716 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1721 if (start || (was_running && restart_required)) {
1722 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1733 EngineControl::post_push ()
1735 /* get a pointer to the current state object, creating one if
1739 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1742 state = save_state ();
1748 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1749 (*i)->active = false;
1752 /* mark this one active (to be used next time the dialog is
1756 state->active = true;
1758 if (_have_control) { // XXX
1759 manage_control_app_sensitivity ();
1762 /* schedule a redisplay of MIDI ports */
1763 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1768 EngineControl::get_rate () const
1770 float r = atof (sample_rate_combo.get_active_text ());
1771 /* the string may have been translated with an abbreviation for
1772 * thousands, so use a crude heuristic to fix this.
1782 EngineControl::get_buffer_size () const
1784 string txt = buffer_size_combo.get_active_text ();
1787 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1795 EngineControl::get_midi_option () const
1797 return midi_option_combo.get_active_text();
1801 EngineControl::get_input_channels() const
1803 if (ARDOUR::Profile->get_mixbus()) {
1804 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1805 if (!backend) return 0;
1806 return backend->input_channels();
1808 return (uint32_t) input_channels_adjustment.get_value();
1812 EngineControl::get_output_channels() const
1814 if (ARDOUR::Profile->get_mixbus()) {
1815 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1816 if (!backend) return 0;
1817 return backend->input_channels();
1819 return (uint32_t) output_channels_adjustment.get_value();
1823 EngineControl::get_input_latency() const
1825 return (uint32_t) input_latency_adjustment.get_value();
1829 EngineControl::get_output_latency() const
1831 return (uint32_t) output_latency_adjustment.get_value();
1835 EngineControl::get_backend () const
1837 return backend_combo.get_active_text ();
1841 EngineControl::get_driver () const
1843 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1844 return driver_combo.get_active_text ();
1851 EngineControl::get_device_name () const
1853 return device_combo.get_active_text ();
1857 EngineControl::control_app_button_clicked ()
1859 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1865 backend->launch_control_app ();
1869 EngineControl::manage_control_app_sensitivity ()
1871 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1877 string appname = backend->control_app_name();
1879 if (appname.empty()) {
1880 control_app_button.set_sensitive (false);
1882 control_app_button.set_sensitive (true);
1887 EngineControl::set_desired_sample_rate (uint32_t sr)
1889 _desired_sample_rate = sr;
1894 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1896 if (page_num == 0) {
1897 cancel_button->set_sensitive (true);
1898 ok_button->set_sensitive (true);
1899 apply_button->set_sensitive (true);
1900 _measure_midi.reset();
1902 cancel_button->set_sensitive (false);
1903 ok_button->set_sensitive (false);
1904 apply_button->set_sensitive (false);
1907 if (page_num == midi_tab) {
1909 refresh_midi_display ();
1912 if (page_num == latency_tab) {
1915 if (ARDOUR::AudioEngine::instance()->running()) {
1916 // TODO - mark as 'stopped for latency
1917 ARDOUR_UI::instance()->disconnect_from_engine ();
1921 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1923 /* save any existing latency values */
1925 uint32_t il = (uint32_t) input_latency.get_value ();
1926 uint32_t ol = (uint32_t) input_latency.get_value ();
1928 /* reset to zero so that our new test instance
1929 will be clean of any existing latency measures.
1931 NB. this should really be done by the backend
1932 when stated for latency measurement.
1935 input_latency.set_value (0);
1936 output_latency.set_value (0);
1938 push_state_to_backend (false);
1942 input_latency.set_value (il);
1943 output_latency.set_value (ol);
1946 // This should be done in push_state_to_backend()
1947 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1948 disable_latency_tab ();
1951 enable_latency_tab ();
1955 end_latency_detection ();
1956 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1961 /* latency measurement */
1964 EngineControl::check_audio_latency_measurement ()
1966 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1968 if (mtdm->resolve () < 0) {
1969 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1973 if (mtdm->err () > 0.3) {
1979 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1981 if (sample_rate == 0) {
1982 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1983 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1987 int frames_total = mtdm->del();
1988 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1990 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1991 _("Detected roundtrip latency: "),
1992 frames_total, frames_total * 1000.0f/sample_rate,
1993 _("Systemic latency: "),
1994 extra, extra * 1000.0f/sample_rate);
1998 if (mtdm->err () > 0.2) {
2000 strcat (buf, _("(signal detection error)"));
2006 strcat (buf, _("(inverted - bad wiring)"));
2010 lm_results.set_markup (string_compose (results_markup, buf));
2013 have_lm_results = true;
2014 end_latency_detection ();
2015 lm_use_button.set_sensitive (true);
2023 EngineControl::check_midi_latency_measurement ()
2025 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2027 if (!mididm->have_signal () || mididm->latency () == 0) {
2028 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2033 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2035 if (sample_rate == 0) {
2036 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2037 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2041 ARDOUR::framecnt_t frames_total = mididm->latency();
2042 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2043 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2044 _("Detected roundtrip latency: "),
2045 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2046 _("Systemic latency: "),
2047 extra, extra * 1000.0f / sample_rate);
2051 if (!mididm->ok ()) {
2053 strcat (buf, _("(averaging)"));
2057 if (mididm->deviation () > 50.0) {
2059 strcat (buf, _("(too large jitter)"));
2061 } else if (mididm->deviation () > 10.0) {
2063 strcat (buf, _("(large jitter)"));
2067 have_lm_results = true;
2068 end_latency_detection ();
2069 lm_use_button.set_sensitive (true);
2070 lm_results.set_markup (string_compose (results_markup, buf));
2072 } else if (mididm->processed () > 400) {
2073 have_lm_results = false;
2074 end_latency_detection ();
2075 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2079 lm_results.set_markup (string_compose (results_markup, buf));
2085 EngineControl::start_latency_detection ()
2087 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2088 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2090 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2091 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2092 if (_measure_midi) {
2093 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2095 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2097 lm_measure_label.set_text (_("Cancel"));
2098 have_lm_results = false;
2099 lm_use_button.set_sensitive (false);
2100 lm_input_channel_combo.set_sensitive (false);
2101 lm_output_channel_combo.set_sensitive (false);
2107 EngineControl::end_latency_detection ()
2109 latency_timeout.disconnect ();
2110 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2111 lm_measure_label.set_text (_("Measure"));
2112 if (!have_lm_results) {
2113 lm_use_button.set_sensitive (false);
2115 lm_input_channel_combo.set_sensitive (true);
2116 lm_output_channel_combo.set_sensitive (true);
2121 EngineControl::latency_button_clicked ()
2124 start_latency_detection ();
2126 end_latency_detection ();
2131 EngineControl::use_latency_button_clicked ()
2133 if (_measure_midi) {
2134 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2138 ARDOUR::framecnt_t frames_total = mididm->latency();
2139 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2140 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2141 _measure_midi->input_latency = one_way;
2142 _measure_midi->output_latency = one_way;
2143 notebook.set_current_page (midi_tab);
2145 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2151 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2152 one_way = std::max (0., one_way);
2154 input_latency_adjustment.set_value (one_way);
2155 output_latency_adjustment.set_value (one_way);
2157 /* back to settings page */
2158 notebook.set_current_page (0);
2164 EngineControl::on_delete_event (GdkEventAny* ev)
2166 if (notebook.get_current_page() == 2) {
2167 /* currently on latency tab - be sure to clean up */
2168 end_latency_detection ();
2170 return ArdourDialog::on_delete_event (ev);
2174 EngineControl::engine_running ()
2176 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2179 buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
2180 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2182 buffer_size_combo.set_sensitive (true);
2183 sample_rate_combo.set_sensitive (true);
2185 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2186 connect_disconnect_button.show();
2188 started_at_least_once = true;
2189 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2193 EngineControl::engine_stopped ()
2195 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2198 buffer_size_combo.set_sensitive (false);
2199 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2200 connect_disconnect_button.show();
2202 sample_rate_combo.set_sensitive (true);
2203 buffer_size_combo.set_sensitive (true);
2204 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2208 EngineControl::device_list_changed ()
2210 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
2212 midi_option_changed();
2216 EngineControl::connect_disconnect_click()
2218 if (ARDOUR::AudioEngine::instance()->running()) {
2219 ARDOUR_UI::instance()->disconnect_from_engine ();
2221 ARDOUR_UI::instance()->reconnect_to_engine ();
2226 EngineControl::calibrate_audio_latency ()
2228 _measure_midi.reset ();
2229 have_lm_results = false;
2230 lm_use_button.set_sensitive (false);
2231 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2232 notebook.set_current_page (latency_tab);
2236 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2239 have_lm_results = false;
2240 lm_use_button.set_sensitive (false);
2241 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2242 notebook.set_current_page (latency_tab);
2246 EngineControl::configure_midi_devices ()
2248 notebook.set_current_page (midi_tab);