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()) {
801 uint32_t cnt = (uint32_t) sb->get_value();
803 sb->set_text (_("all available channels"));
806 snprintf (buf, sizeof (buf), "%d", cnt);
813 EngineControl::list_devices ()
815 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
818 /* now fill out devices, mark sample rates, buffer sizes insensitive */
820 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
822 /* NOTE: Ardour currently does not display the "available" field of the
825 * Doing so would require a different GUI widget than the combo
826 * box/popdown that we currently use, since it has no way to list
827 * items that are not selectable. Something more like a popup menu,
828 * which could have unselectable items, would be appropriate.
831 vector<string> available_devices;
833 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
834 available_devices.push_back (i->name);
837 if (!available_devices.empty()) {
839 update_sensitivity ();
842 string current_device, found_device;
843 current_device = device_combo.get_active_text ();
844 if (current_device == "") {
845 current_device = backend->device_name ();
848 // Make sure that the active text is still relevant for this
849 // device (it might only be relevant to the previous device!!)
850 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
851 if (*i == current_device)
852 found_device = current_device;
854 if (found_device == "")
855 // device has never been set (or was not relevant
856 // for this backend) Let's make sure it's not blank
857 current_device = available_devices.front ();
859 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
860 set_popdown_strings (device_combo, available_devices);
862 device_combo.set_active_text (current_device);
867 input_latency.set_sensitive (true);
868 output_latency.set_sensitive (true);
869 input_channels.set_sensitive (true);
870 output_channels.set_sensitive (true);
872 ok_button->set_sensitive (true);
873 apply_button->set_sensitive (true);
876 device_combo.clear();
877 sample_rate_combo.set_sensitive (false);
878 buffer_size_combo.set_sensitive (false);
879 input_latency.set_sensitive (false);
880 output_latency.set_sensitive (false);
881 input_channels.set_sensitive (false);
882 output_channels.set_sensitive (false);
884 ok_button->set_sensitive (false);
885 apply_button->set_sensitive (false);
887 ok_button->set_sensitive (true);
888 apply_button->set_sensitive (true);
889 if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
890 sample_rate_combo.set_sensitive (true);
892 if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
893 buffer_size_combo.set_sensitive (true);
901 EngineControl::driver_changed ()
903 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
906 backend->set_driver (driver_combo.get_active_text());
909 if (!ignore_changes) {
910 maybe_display_saved_state ();
915 EngineControl::device_changed ()
918 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
920 string device_name = device_combo.get_active_text ();
923 if (device_name != backend->device_name()) {
924 /* we set the backend-device to query various device related intormation.
925 * This has the side effect that backend->device_name() will match
926 * the device_name and 'change_device' will never be true.
927 * so work around this by setting...
929 queue_device_changed = true;
932 //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
933 backend->set_device_name(device_name);
936 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
938 /* don't allow programmatic change to combos to cause a
939 recursive call to this method.
949 sr = backend->available_sample_rates (device_name);
952 sr.push_back (8000.0f);
953 sr.push_back (16000.0f);
954 sr.push_back (32000.0f);
955 sr.push_back (44100.0f);
956 sr.push_back (48000.0f);
957 sr.push_back (88200.0f);
958 sr.push_back (96000.0f);
959 sr.push_back (192000.0f);
960 sr.push_back (384000.0f);
963 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
964 s.push_back (rate_as_string (*x));
965 if (*x == _desired_sample_rate) {
971 sample_rate_combo.set_sensitive (true);
972 set_popdown_strings (sample_rate_combo, s);
974 if (desired.empty()) {
975 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
977 sample_rate_combo.set_active_text (desired);
981 sample_rate_combo.set_sensitive (false);
989 bs = backend->available_buffer_sizes (device_name);
990 } else if (backend->can_change_buffer_size_when_running()) {
1000 bs.push_back (4096);
1001 bs.push_back (8192);
1004 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1005 s.push_back (bufsize_as_string (*x));
1009 buffer_size_combo.set_sensitive (true);
1010 set_popdown_strings (buffer_size_combo, s);
1012 uint32_t period = backend->buffer_size();
1014 period = backend->default_buffer_size(device_name);
1016 set_active_text_if_present (buffer_size_combo, bufsize_as_string (period));
1017 show_buffer_duration ();
1019 buffer_size_combo.set_sensitive (false);
1022 /* XXX theoretically need to set min + max channel counts here
1025 manage_control_app_sensitivity ();
1028 /* pick up any saved state for this device */
1030 if (!ignore_changes) {
1031 maybe_display_saved_state ();
1036 EngineControl::bufsize_as_string (uint32_t sz)
1038 /* Translators: "samples" is always plural here, so no
1039 need for plural+singular forms.
1042 snprintf (buf, sizeof (buf), _("%u samples"), sz);
1047 EngineControl::sample_rate_changed ()
1049 /* reset the strings for buffer size to show the correct msec value
1050 (reflecting the new sample rate).
1053 show_buffer_duration ();
1054 if (!ignore_changes) {
1061 EngineControl::buffer_size_changed ()
1063 show_buffer_duration ();
1064 if (!ignore_changes) {
1070 EngineControl::show_buffer_duration ()
1073 /* buffer sizes - convert from just samples to samples + msecs for
1074 * the displayed string
1077 string bs_text = buffer_size_combo.get_active_text ();
1078 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1079 uint32_t rate = get_rate();
1081 /* Developers: note the hard-coding of a double buffered model
1082 in the (2 * samples) computation of latency. we always start
1083 the audiobackend in this configuration.
1085 /* note to jack1 developers: ardour also always starts the engine
1086 * in async mode (no jack2 --sync option) which adds an extra cycle
1087 * of latency with jack2 (and *3 would be correct)
1088 * The value can also be wrong if jackd is started externally..
1090 * At the time of writing the ALSA backend always uses double-buffering *2,
1091 * The Dummy backend *1, and who knows what ASIO really does :)
1093 * So just display the period size, that's also what
1094 * ARDOUR_UI::update_sample_rate() does for the status bar.
1095 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1096 * but still, that's the buffer period, not [round-trip] latency)
1099 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1100 buffer_size_duration_label.set_text (buf);
1104 EngineControl::midi_option_changed ()
1106 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1109 backend->set_midi_option (get_midi_option());
1111 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1113 //_midi_devices.clear(); // TODO merge with state-saved settings..
1114 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1115 std::vector<MidiDeviceSettings> new_devices;
1117 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1118 MidiDeviceSettings mds = find_midi_device (i->name);
1119 if (i->available && !mds) {
1120 uint32_t input_latency = 0;
1121 uint32_t output_latency = 0;
1122 if (_can_set_midi_latencies) {
1123 input_latency = backend->systemic_midi_input_latency (i->name);
1124 output_latency = backend->systemic_midi_output_latency (i->name);
1126 bool enabled = backend->midi_device_enabled (i->name);
1127 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1128 new_devices.push_back (ptr);
1129 } else if (i->available) {
1130 new_devices.push_back (mds);
1133 _midi_devices = new_devices;
1135 if (_midi_devices.empty()) {
1136 midi_devices_button.set_sensitive (false);
1138 midi_devices_button.set_sensitive (true);
1141 if (!ignore_changes) {
1147 EngineControl::parameter_changed ()
1149 if (!ignore_changes) {
1154 EngineControl::State
1155 EngineControl::get_matching_state (
1156 const string& backend,
1157 const string& driver,
1158 const string& device)
1160 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1161 if ((*i)->backend == backend &&
1162 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1170 EngineControl::State
1171 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1173 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1176 return get_matching_state (backend_combo.get_active_text(),
1177 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1178 device_combo.get_active_text());
1182 return get_matching_state (backend_combo.get_active_text(),
1184 device_combo.get_active_text());
1187 EngineControl::State
1188 EngineControl::save_state ()
1192 if (!_have_control) {
1193 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1197 state.reset(new StateStruct);
1198 state->backend = get_backend ();
1200 state.reset(new StateStruct);
1201 store_state (state);
1204 for (StateList::iterator i = states.begin(); i != states.end();) {
1205 if ((*i)->backend == state->backend &&
1206 (*i)->driver == state->driver &&
1207 (*i)->device == state->device) {
1208 i = states.erase(i);
1214 states.push_back (state);
1220 EngineControl::store_state (State state)
1222 state->backend = get_backend ();
1223 state->driver = get_driver ();
1224 state->device = get_device_name ();
1225 state->sample_rate = get_rate ();
1226 state->buffer_size = get_buffer_size ();
1227 state->input_latency = get_input_latency ();
1228 state->output_latency = get_output_latency ();
1229 state->input_channels = get_input_channels ();
1230 state->output_channels = get_output_channels ();
1231 state->midi_option = get_midi_option ();
1232 state->midi_devices = _midi_devices;
1236 EngineControl::maybe_display_saved_state ()
1238 if (!_have_control) {
1242 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1245 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1247 if (!_desired_sample_rate) {
1248 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1250 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1251 /* call this explicitly because we're ignoring changes to
1252 the controls at this point.
1254 show_buffer_duration ();
1255 input_latency.set_value (state->input_latency);
1256 output_latency.set_value (state->output_latency);
1258 if (!state->midi_option.empty()) {
1259 midi_option_combo.set_active_text (state->midi_option);
1260 _midi_devices = state->midi_devices;
1266 EngineControl::get_state ()
1268 XMLNode* root = new XMLNode ("AudioMIDISetup");
1271 if (!states.empty()) {
1272 XMLNode* state_nodes = new XMLNode ("EngineStates");
1274 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1276 XMLNode* node = new XMLNode ("State");
1278 node->add_property ("backend", (*i)->backend);
1279 node->add_property ("driver", (*i)->driver);
1280 node->add_property ("device", (*i)->device);
1281 node->add_property ("sample-rate", (*i)->sample_rate);
1282 node->add_property ("buffer-size", (*i)->buffer_size);
1283 node->add_property ("input-latency", (*i)->input_latency);
1284 node->add_property ("output-latency", (*i)->output_latency);
1285 node->add_property ("input-channels", (*i)->input_channels);
1286 node->add_property ("output-channels", (*i)->output_channels);
1287 node->add_property ("active", (*i)->active ? "yes" : "no");
1288 node->add_property ("midi-option", (*i)->midi_option);
1290 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1291 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1292 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1293 midi_device_stuff->add_property (X_("name"), (*p)->name);
1294 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1295 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1296 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1297 midi_devices->add_child_nocopy (*midi_device_stuff);
1299 node->add_child_nocopy (*midi_devices);
1301 state_nodes->add_child_nocopy (*node);
1304 root->add_child_nocopy (*state_nodes);
1311 EngineControl::set_state (const XMLNode& root)
1313 XMLNodeList clist, cclist;
1314 XMLNodeConstIterator citer, cciter;
1316 XMLNode* grandchild;
1317 XMLProperty* prop = NULL;
1319 if (root.name() != "AudioMIDISetup") {
1323 clist = root.children();
1327 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1331 if (child->name() != "EngineStates") {
1335 cclist = child->children();
1337 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1338 State state (new StateStruct);
1340 grandchild = *cciter;
1342 if (grandchild->name() != "State") {
1346 if ((prop = grandchild->property ("backend")) == 0) {
1349 state->backend = prop->value ();
1351 if ((prop = grandchild->property ("driver")) == 0) {
1354 state->driver = prop->value ();
1356 if ((prop = grandchild->property ("device")) == 0) {
1359 state->device = prop->value ();
1361 if ((prop = grandchild->property ("sample-rate")) == 0) {
1364 state->sample_rate = atof (prop->value ());
1366 if ((prop = grandchild->property ("buffer-size")) == 0) {
1369 state->buffer_size = atoi (prop->value ());
1371 if ((prop = grandchild->property ("input-latency")) == 0) {
1374 state->input_latency = atoi (prop->value ());
1376 if ((prop = grandchild->property ("output-latency")) == 0) {
1379 state->output_latency = atoi (prop->value ());
1381 if ((prop = grandchild->property ("input-channels")) == 0) {
1384 state->input_channels = atoi (prop->value ());
1386 if ((prop = grandchild->property ("output-channels")) == 0) {
1389 state->output_channels = atoi (prop->value ());
1391 if ((prop = grandchild->property ("active")) == 0) {
1394 state->active = string_is_affirmative (prop->value ());
1396 if ((prop = grandchild->property ("midi-option")) == 0) {
1399 state->midi_option = prop->value ();
1401 state->midi_devices.clear();
1403 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1404 const XMLNodeList mnc = midinode->children();
1405 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1406 if ((*n)->property (X_("name")) == 0
1407 || (*n)->property (X_("enabled")) == 0
1408 || (*n)->property (X_("input-latency")) == 0
1409 || (*n)->property (X_("output-latency")) == 0
1414 MidiDeviceSettings ptr (new MidiDeviceSetting(
1415 (*n)->property (X_("name"))->value (),
1416 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1417 atoi ((*n)->property (X_("input-latency"))->value ()),
1418 atoi ((*n)->property (X_("output-latency"))->value ())
1420 state->midi_devices.push_back (ptr);
1425 /* remove accumulated duplicates (due to bug in ealier version)
1426 * this can be removed again before release
1428 for (StateList::iterator i = states.begin(); i != states.end();) {
1429 if ((*i)->backend == state->backend &&
1430 (*i)->driver == state->driver &&
1431 (*i)->device == state->device) {
1432 i = states.erase(i);
1439 states.push_back (state);
1443 /* now see if there was an active state and switch the setup to it */
1445 // purge states of backend that are not available in this built
1446 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1447 vector<std::string> backend_names;
1449 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1450 backend_names.push_back((*i)->name);
1452 for (StateList::iterator i = states.begin(); i != states.end();) {
1453 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1454 i = states.erase(i);
1460 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1464 backend_combo.set_active_text ((*i)->backend);
1465 driver_combo.set_active_text ((*i)->driver);
1466 device_combo.set_active_text ((*i)->device);
1467 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1468 set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
1469 input_latency.set_value ((*i)->input_latency);
1470 output_latency.set_value ((*i)->output_latency);
1471 midi_option_combo.set_active_text ((*i)->midi_option);
1479 EngineControl::push_state_to_backend (bool start)
1481 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1487 /* figure out what is going to change */
1489 bool restart_required = false;
1490 bool was_running = ARDOUR::AudioEngine::instance()->running();
1491 bool change_driver = false;
1492 bool change_device = false;
1493 bool change_rate = false;
1494 bool change_bufsize = false;
1495 bool change_latency = false;
1496 bool change_channels = false;
1497 bool change_midi = false;
1499 uint32_t ochan = get_output_channels ();
1500 uint32_t ichan = get_input_channels ();
1502 if (_have_control) {
1504 if (started_at_least_once) {
1506 /* we can control the backend */
1508 if (backend->requires_driver_selection()) {
1509 if (get_driver() != backend->driver_name()) {
1510 change_driver = true;
1514 if (queue_device_changed || get_device_name() != backend->device_name()) {
1515 change_device = true;
1518 if (get_rate() != backend->sample_rate()) {
1522 if (get_buffer_size() != backend->buffer_size()) {
1523 change_bufsize = true;
1526 if (get_midi_option() != backend->midi_option()) {
1530 /* zero-requested channels means "all available" */
1533 ichan = backend->input_channels();
1537 ochan = backend->output_channels();
1540 if (ichan != backend->input_channels()) {
1541 change_channels = true;
1544 if (ochan != backend->output_channels()) {
1545 change_channels = true;
1548 if (get_input_latency() != backend->systemic_input_latency() ||
1549 get_output_latency() != backend->systemic_output_latency()) {
1550 change_latency = true;
1553 /* backend never started, so we have to force a group
1556 change_device = true;
1557 if (backend->requires_driver_selection()) {
1558 change_driver = true;
1561 change_bufsize = true;
1562 change_channels = true;
1563 change_latency = true;
1569 /* we have no control over the backend, meaning that we can
1570 * only possibly change sample rate and buffer size.
1574 if (get_rate() != backend->sample_rate()) {
1575 change_bufsize = true;
1578 if (get_buffer_size() != backend->buffer_size()) {
1579 change_bufsize = true;
1583 queue_device_changed = false;
1585 if (!_have_control) {
1587 /* We do not have control over the backend, so the best we can
1588 * do is try to change the sample rate and/or bufsize and get
1592 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1596 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1601 backend->set_sample_rate (get_rate());
1604 if (change_bufsize) {
1605 backend->set_buffer_size (get_buffer_size());
1609 if (ARDOUR::AudioEngine::instance()->start ()) {
1610 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1620 /* determine if we need to stop the backend before changing parameters */
1622 if (change_driver || change_device || change_channels || change_latency ||
1623 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1625 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1626 restart_required = true;
1628 restart_required = false;
1633 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1634 /* no changes in any parameters that absolutely require a
1635 * restart, so check those that might be changeable without a
1639 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1640 /* can't do this while running ... */
1641 restart_required = true;
1644 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1645 /* can't do this while running ... */
1646 restart_required = true;
1652 if (restart_required) {
1653 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1660 if (change_driver && backend->set_driver (get_driver())) {
1661 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1664 if (change_device && backend->set_device_name (get_device_name())) {
1665 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1668 if (change_rate && backend->set_sample_rate (get_rate())) {
1669 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1672 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1673 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1677 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1678 if (backend->set_input_channels (get_input_channels())) {
1679 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1682 if (backend->set_output_channels (get_output_channels())) {
1683 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1687 if (change_latency) {
1688 if (backend->set_systemic_input_latency (get_input_latency())) {
1689 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1692 if (backend->set_systemic_output_latency (get_output_latency())) {
1693 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1699 backend->set_midi_option (get_midi_option());
1703 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1704 if (_measure_midi) {
1705 if (*p == _measure_midi) {
1706 backend->set_midi_device_enabled ((*p)->name, true);
1708 backend->set_midi_device_enabled ((*p)->name, false);
1712 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1713 if (backend->can_set_systemic_midi_latencies()) {
1714 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1715 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1720 if (start || (was_running && restart_required)) {
1721 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1732 EngineControl::post_push ()
1734 /* get a pointer to the current state object, creating one if
1738 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1741 state = save_state ();
1747 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1748 (*i)->active = false;
1751 /* mark this one active (to be used next time the dialog is
1755 state->active = true;
1757 if (_have_control) { // XXX
1758 manage_control_app_sensitivity ();
1761 /* schedule a redisplay of MIDI ports */
1762 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1767 EngineControl::get_rate () const
1769 float r = atof (sample_rate_combo.get_active_text ());
1770 /* the string may have been translated with an abbreviation for
1771 * thousands, so use a crude heuristic to fix this.
1781 EngineControl::get_buffer_size () const
1783 string txt = buffer_size_combo.get_active_text ();
1786 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1787 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
1788 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
1796 EngineControl::get_midi_option () const
1798 return midi_option_combo.get_active_text();
1802 EngineControl::get_input_channels() const
1804 if (ARDOUR::Profile->get_mixbus()) {
1805 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1806 if (!backend) return 0;
1807 return backend->input_channels();
1809 return (uint32_t) input_channels_adjustment.get_value();
1813 EngineControl::get_output_channels() const
1815 if (ARDOUR::Profile->get_mixbus()) {
1816 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1817 if (!backend) return 0;
1818 return backend->input_channels();
1820 return (uint32_t) output_channels_adjustment.get_value();
1824 EngineControl::get_input_latency() const
1826 return (uint32_t) input_latency_adjustment.get_value();
1830 EngineControl::get_output_latency() const
1832 return (uint32_t) output_latency_adjustment.get_value();
1836 EngineControl::get_backend () const
1838 return backend_combo.get_active_text ();
1842 EngineControl::get_driver () const
1844 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1845 return driver_combo.get_active_text ();
1852 EngineControl::get_device_name () const
1854 return device_combo.get_active_text ();
1858 EngineControl::control_app_button_clicked ()
1860 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1866 backend->launch_control_app ();
1870 EngineControl::manage_control_app_sensitivity ()
1872 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1878 string appname = backend->control_app_name();
1880 if (appname.empty()) {
1881 control_app_button.set_sensitive (false);
1883 control_app_button.set_sensitive (true);
1888 EngineControl::set_desired_sample_rate (uint32_t sr)
1890 _desired_sample_rate = sr;
1895 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1897 if (page_num == 0) {
1898 cancel_button->set_sensitive (true);
1899 ok_button->set_sensitive (true);
1900 apply_button->set_sensitive (true);
1901 _measure_midi.reset();
1903 cancel_button->set_sensitive (false);
1904 ok_button->set_sensitive (false);
1905 apply_button->set_sensitive (false);
1908 if (page_num == midi_tab) {
1910 refresh_midi_display ();
1913 if (page_num == latency_tab) {
1916 if (ARDOUR::AudioEngine::instance()->running()) {
1917 // TODO - mark as 'stopped for latency
1918 ARDOUR_UI::instance()->disconnect_from_engine ();
1922 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1924 /* save any existing latency values */
1926 uint32_t il = (uint32_t) input_latency.get_value ();
1927 uint32_t ol = (uint32_t) input_latency.get_value ();
1929 /* reset to zero so that our new test instance
1930 will be clean of any existing latency measures.
1932 NB. this should really be done by the backend
1933 when stated for latency measurement.
1936 input_latency.set_value (0);
1937 output_latency.set_value (0);
1939 push_state_to_backend (false);
1943 input_latency.set_value (il);
1944 output_latency.set_value (ol);
1947 // This should be done in push_state_to_backend()
1948 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1949 disable_latency_tab ();
1952 enable_latency_tab ();
1956 end_latency_detection ();
1957 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1962 /* latency measurement */
1965 EngineControl::check_audio_latency_measurement ()
1967 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1969 if (mtdm->resolve () < 0) {
1970 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1974 if (mtdm->err () > 0.3) {
1980 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1982 if (sample_rate == 0) {
1983 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1984 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1988 int frames_total = mtdm->del();
1989 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1991 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1992 _("Detected roundtrip latency: "),
1993 frames_total, frames_total * 1000.0f/sample_rate,
1994 _("Systemic latency: "),
1995 extra, extra * 1000.0f/sample_rate);
1999 if (mtdm->err () > 0.2) {
2001 strcat (buf, _("(signal detection error)"));
2007 strcat (buf, _("(inverted - bad wiring)"));
2011 lm_results.set_markup (string_compose (results_markup, buf));
2014 have_lm_results = true;
2015 end_latency_detection ();
2016 lm_use_button.set_sensitive (true);
2024 EngineControl::check_midi_latency_measurement ()
2026 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2028 if (!mididm->have_signal () || mididm->latency () == 0) {
2029 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2034 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2036 if (sample_rate == 0) {
2037 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2038 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2042 ARDOUR::framecnt_t frames_total = mididm->latency();
2043 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2044 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2045 _("Detected roundtrip latency: "),
2046 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2047 _("Systemic latency: "),
2048 extra, extra * 1000.0f / sample_rate);
2052 if (!mididm->ok ()) {
2054 strcat (buf, _("(averaging)"));
2058 if (mididm->deviation () > 50.0) {
2060 strcat (buf, _("(too large jitter)"));
2062 } else if (mididm->deviation () > 10.0) {
2064 strcat (buf, _("(large jitter)"));
2068 have_lm_results = true;
2069 end_latency_detection ();
2070 lm_use_button.set_sensitive (true);
2071 lm_results.set_markup (string_compose (results_markup, buf));
2073 } else if (mididm->processed () > 400) {
2074 have_lm_results = false;
2075 end_latency_detection ();
2076 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2080 lm_results.set_markup (string_compose (results_markup, buf));
2086 EngineControl::start_latency_detection ()
2088 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2089 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2091 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2092 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2093 if (_measure_midi) {
2094 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2096 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2098 lm_measure_label.set_text (_("Cancel"));
2099 have_lm_results = false;
2100 lm_use_button.set_sensitive (false);
2101 lm_input_channel_combo.set_sensitive (false);
2102 lm_output_channel_combo.set_sensitive (false);
2108 EngineControl::end_latency_detection ()
2110 latency_timeout.disconnect ();
2111 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2112 lm_measure_label.set_text (_("Measure"));
2113 if (!have_lm_results) {
2114 lm_use_button.set_sensitive (false);
2116 lm_input_channel_combo.set_sensitive (true);
2117 lm_output_channel_combo.set_sensitive (true);
2122 EngineControl::latency_button_clicked ()
2125 start_latency_detection ();
2127 end_latency_detection ();
2132 EngineControl::use_latency_button_clicked ()
2134 if (_measure_midi) {
2135 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2139 ARDOUR::framecnt_t frames_total = mididm->latency();
2140 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2141 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2142 _measure_midi->input_latency = one_way;
2143 _measure_midi->output_latency = one_way;
2144 notebook.set_current_page (midi_tab);
2146 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2152 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2153 one_way = std::max (0., one_way);
2155 input_latency_adjustment.set_value (one_way);
2156 output_latency_adjustment.set_value (one_way);
2158 /* back to settings page */
2159 notebook.set_current_page (0);
2165 EngineControl::on_delete_event (GdkEventAny* ev)
2167 if (notebook.get_current_page() == 2) {
2168 /* currently on latency tab - be sure to clean up */
2169 end_latency_detection ();
2171 return ArdourDialog::on_delete_event (ev);
2175 EngineControl::engine_running ()
2177 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2180 set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2181 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2183 buffer_size_combo.set_sensitive (true);
2184 sample_rate_combo.set_sensitive (true);
2186 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2187 connect_disconnect_button.show();
2189 started_at_least_once = true;
2190 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2194 EngineControl::engine_stopped ()
2196 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2199 buffer_size_combo.set_sensitive (false);
2200 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2201 connect_disconnect_button.show();
2203 sample_rate_combo.set_sensitive (true);
2204 buffer_size_combo.set_sensitive (true);
2205 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2209 EngineControl::device_list_changed ()
2211 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
2213 midi_option_changed();
2217 EngineControl::connect_disconnect_click()
2219 if (ARDOUR::AudioEngine::instance()->running()) {
2220 ARDOUR_UI::instance()->disconnect_from_engine ();
2222 ARDOUR_UI::instance()->reconnect_to_engine ();
2227 EngineControl::calibrate_audio_latency ()
2229 _measure_midi.reset ();
2230 have_lm_results = false;
2231 lm_use_button.set_sensitive (false);
2232 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2233 notebook.set_current_page (latency_tab);
2237 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2240 have_lm_results = false;
2241 lm_use_button.set_sensitive (false);
2242 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2243 notebook.set_current_page (latency_tab);
2247 EngineControl::configure_midi_devices ()
2249 notebook.set_current_page (midi_tab);