2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
30 #include "pbd/error.h"
31 #include "pbd/xml++.h"
32 #include "pbd/unwind.h"
33 #include "pbd/failed_constructor.h"
35 #include <gtkmm/alignment.h>
36 #include <gtkmm/stock.h>
37 #include <gtkmm/notebook.h>
38 #include <gtkmm2ext/utils.h>
40 #include "ardour/audio_backend.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/mtdm.h"
43 #include "ardour/mididm.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/types.h"
46 #include "ardour/profile.h"
48 #include "pbd/convert.h"
49 #include "pbd/error.h"
51 #include "ardour_ui.h"
52 #include "engine_dialog.h"
53 #include "gui_thread.h"
59 using namespace Gtkmm2ext;
62 using namespace ARDOUR_UI_UTILS;
64 static const unsigned int midi_tab = 2;
65 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
67 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
69 EngineControl::EngineControl ()
70 : ArdourDialog (_("Audio/MIDI Setup"))
73 , input_latency_adjustment (0, 0, 99999, 1)
74 , input_latency (input_latency_adjustment)
75 , output_latency_adjustment (0, 0, 99999, 1)
76 , output_latency (output_latency_adjustment)
77 , input_channels_adjustment (0, 0, 256, 1)
78 , input_channels (input_channels_adjustment)
79 , output_channels_adjustment (0, 0, 256, 1)
80 , output_channels (output_channels_adjustment)
81 , ports_adjustment (128, 8, 1024, 1, 16)
82 , ports_spinner (ports_adjustment)
83 , control_app_button (_("Device Control Panel"))
84 , midi_devices_button (_("Midi Device Setup"))
85 , lm_measure_label (_("Measure"))
86 , lm_use_button (_("Use results"))
87 , lm_back_button (_("Back to settings ... (ignore results)"))
88 , lm_button_audio (_("Calibrate Audio"))
90 , have_lm_results (false)
92 , midi_back_button (_("Back to settings"))
94 , _desired_sample_rate (0)
95 , started_at_least_once (false)
96 , queue_device_changed (false)
98 using namespace Notebook_Helpers;
99 vector<string> backend_names;
101 AttachOptions xopt = AttachOptions (FILL|EXPAND);
104 set_name (X_("AudioMIDISetup"));
106 /* the backend combo is the one thing that is ALWAYS visible */
108 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
110 if (backends.empty()) {
111 MessageDialog msg (string_compose (_("No audio/MIDI backends detected. %1 cannot run\n\n(This is a build/packaging/system error. It should never happen.)"), PROGRAM_NAME));
113 throw failed_constructor ();
116 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
117 backend_names.push_back ((*b)->name);
120 set_popdown_strings (backend_combo, backend_names);
121 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
123 /* setup basic packing characteristics for the table used on the main
124 * tab of the notebook
127 basic_packer.set_spacings (6);
128 basic_packer.set_border_width (12);
129 basic_packer.set_homogeneous (false);
133 basic_hbox.pack_start (basic_packer, false, false);
135 /* latency measurement tab */
137 lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
140 lm_table.set_row_spacings (12);
141 lm_table.set_col_spacings (6);
142 lm_table.set_homogeneous (false);
144 lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
147 lm_preamble.set_width_chars (60);
148 lm_preamble.set_line_wrap (true);
149 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
151 lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
154 Gtk::Label* preamble;
155 preamble = manage (new Label);
156 preamble->set_width_chars (60);
157 preamble->set_line_wrap (true);
158 preamble->set_markup (_("Select two channels below and connect them using a cable."));
160 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
163 label = manage (new Label (_("Output channel")));
164 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
166 Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
167 misc_align->add (lm_output_channel_combo);
168 lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
171 label = manage (new Label (_("Input channel")));
172 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
174 misc_align = manage (new Alignment (0.0, 0.5));
175 misc_align->add (lm_input_channel_combo);
176 lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
179 xopt = AttachOptions(0);
181 lm_measure_label.set_padding (10, 10);
182 lm_measure_button.add (lm_measure_label);
183 lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
184 lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
185 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
187 lm_use_button.set_sensitive (false);
189 /* Increase the default spacing around the labels of these three
195 if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
196 l->set_padding (10, 10);
199 if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
200 l->set_padding (10, 10);
203 preamble = manage (new Label);
204 preamble->set_width_chars (60);
205 preamble->set_line_wrap (true);
206 preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
207 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
210 preamble = manage (new Label);
211 preamble->set_width_chars (60);
212 preamble->set_line_wrap (true);
213 preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
214 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
216 ++row; // skip a row in the table
217 ++row; // skip a row in the table
219 lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
221 ++row; // skip a row in the table
222 ++row; // skip a row in the table
224 lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
225 lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
226 lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
228 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
230 lm_vbox.set_border_width (12);
231 lm_vbox.pack_start (lm_table, false, false);
233 midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
237 notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
238 notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
239 notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
240 notebook.set_border_width (12);
242 notebook.set_show_tabs (false);
243 notebook.show_all ();
245 notebook.set_name ("SettingsNotebook");
247 /* packup the notebook */
249 get_vbox()->set_border_width (12);
250 get_vbox()->pack_start (notebook);
252 get_action_area()->pack_start (engine_status);
253 engine_status.show();
255 /* need a special function to print "all available channels" when the
256 * channel counts hit zero.
259 input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
260 output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
262 midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
263 midi_devices_button.set_sensitive (false);
264 midi_devices_button.set_name ("generic button");
265 midi_devices_button.set_can_focus(true);
267 control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
268 manage_control_app_sensitivity ();
270 cancel_button = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL);
271 apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
272 ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
274 /* Pick up any existing audio setup configuration, if appropriate */
276 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
278 ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
279 ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
280 ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
281 ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
284 set_state (*audio_setup);
287 if (backend_combo.get_active_text().empty()) {
288 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
289 backend_combo.set_active_text (backend_names.front());
294 /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
295 if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
296 backend_combo.set_active_text (backend_names.back());
297 /* ignore: don't save state */
298 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
303 /* Connect to signals */
305 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
306 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
307 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
308 device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
309 midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
311 input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
312 output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
313 input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
314 output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
316 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
318 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
319 connect_disconnect_button.set_no_show_all();
324 EngineControl::on_show ()
326 ArdourDialog::on_show ();
327 if (!ARDOUR::AudioEngine::instance()->current_backend() || !ARDOUR::AudioEngine::instance()->running()) {
328 // re-check _have_control (jackd running) see #6041
332 ok_button->grab_focus();
336 EngineControl::on_response (int response_id)
338 ArdourDialog::on_response (response_id);
340 switch (response_id) {
342 push_state_to_backend (true);
345 push_state_to_backend (true);
348 case RESPONSE_DELETE_EVENT:
351 ev.type = GDK_BUTTON_PRESS;
353 on_delete_event ((GdkEventAny*) &ev);
362 EngineControl::build_notebook ()
365 AttachOptions xopt = AttachOptions (FILL|EXPAND);
367 /* clear the table */
369 Gtkmm2ext::container_clear (basic_vbox);
370 Gtkmm2ext::container_clear (basic_packer);
372 if (control_app_button.get_parent()) {
373 control_app_button.get_parent()->remove (control_app_button);
376 label = manage (left_aligned_label (_("Audio System:")));
377 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
378 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
380 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
381 lm_button_audio.set_name ("generic button");
382 lm_button_audio.set_can_focus(true);
385 build_full_control_notebook ();
387 build_no_control_notebook ();
390 basic_vbox.pack_start (basic_hbox, false, false);
393 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
394 basic_vbox.show_all ();
399 EngineControl::build_full_control_notebook ()
401 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
404 using namespace Notebook_Helpers;
406 vector<string> strings;
407 AttachOptions xopt = AttachOptions (FILL|EXPAND);
408 int row = 1; // row zero == backend combo
410 /* start packing it up */
412 if (backend->requires_driver_selection()) {
413 label = manage (left_aligned_label (_("Driver:")));
414 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
415 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
419 label = manage (left_aligned_label (_("Device:")));
420 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
421 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
424 label = manage (left_aligned_label (_("Sample rate:")));
425 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
426 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
430 label = manage (left_aligned_label (_("Buffer size:")));
431 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
432 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
433 buffer_size_duration_label.set_alignment (0.0); /* left-align */
434 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
436 /* button spans 2 rows */
438 basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
441 input_channels.set_name ("InputChannels");
442 input_channels.set_flags (Gtk::CAN_FOCUS);
443 input_channels.set_digits (0);
444 input_channels.set_wrap (false);
445 output_channels.set_editable (true);
447 if (!ARDOUR::Profile->get_mixbus()) {
448 label = manage (left_aligned_label (_("Input Channels:")));
449 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
450 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
454 output_channels.set_name ("OutputChannels");
455 output_channels.set_flags (Gtk::CAN_FOCUS);
456 output_channels.set_digits (0);
457 output_channels.set_wrap (false);
458 output_channels.set_editable (true);
460 if (!ARDOUR::Profile->get_mixbus()) {
461 label = manage (left_aligned_label (_("Output Channels:")));
462 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
463 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
467 input_latency.set_name ("InputLatency");
468 input_latency.set_flags (Gtk::CAN_FOCUS);
469 input_latency.set_digits (0);
470 input_latency.set_wrap (false);
471 input_latency.set_editable (true);
473 label = manage (left_aligned_label (_("Hardware input latency:")));
474 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
475 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
476 label = manage (left_aligned_label (_("samples")));
477 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
480 output_latency.set_name ("OutputLatency");
481 output_latency.set_flags (Gtk::CAN_FOCUS);
482 output_latency.set_digits (0);
483 output_latency.set_wrap (false);
484 output_latency.set_editable (true);
486 label = manage (left_aligned_label (_("Hardware output latency:")));
487 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
488 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
489 label = manage (left_aligned_label (_("samples")));
490 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
492 /* button spans 2 rows */
494 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
497 label = manage (left_aligned_label (_("MIDI System:")));
498 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
499 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
500 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
505 EngineControl::build_no_control_notebook ()
507 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
510 using namespace Notebook_Helpers;
512 vector<string> strings;
513 AttachOptions xopt = AttachOptions (FILL|EXPAND);
514 int row = 1; // row zero == backend combo
515 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
517 label = manage (new Label);
518 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
519 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
522 if (backend->can_change_sample_rate_when_running()) {
523 label = manage (left_aligned_label (_("Sample rate:")));
524 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
525 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
529 if (backend->can_change_buffer_size_when_running()) {
530 label = manage (left_aligned_label (_("Buffer size:")));
531 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
532 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
533 buffer_size_duration_label.set_alignment (0.0); /* left-align */
534 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
538 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
542 EngineControl::~EngineControl ()
544 ignore_changes = true;
548 EngineControl::disable_latency_tab ()
550 vector<string> empty;
551 set_popdown_strings (lm_output_channel_combo, empty);
552 set_popdown_strings (lm_input_channel_combo, empty);
553 lm_measure_button.set_sensitive (false);
554 lm_use_button.set_sensitive (false);
558 EngineControl::enable_latency_tab ()
560 vector<string> outputs;
561 vector<string> inputs;
563 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
564 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
565 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
567 if (!ARDOUR::AudioEngine::instance()->running()) {
568 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
569 notebook.set_current_page (0);
573 else if (inputs.empty() || outputs.empty()) {
574 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
575 notebook.set_current_page (0);
580 lm_back_button_signal.disconnect();
582 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
585 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
589 set_popdown_strings (lm_output_channel_combo, outputs);
590 lm_output_channel_combo.set_active_text (outputs.front());
591 lm_output_channel_combo.set_sensitive (true);
593 set_popdown_strings (lm_input_channel_combo, inputs);
594 lm_input_channel_combo.set_active_text (inputs.front());
595 lm_input_channel_combo.set_sensitive (true);
597 lm_measure_button.set_sensitive (true);
601 EngineControl::setup_midi_tab_for_backend ()
603 string backend = backend_combo.get_active_text ();
605 Gtkmm2ext::container_clear (midi_vbox);
607 midi_vbox.set_border_width (12);
608 midi_device_table.set_border_width (12);
610 if (backend == "JACK") {
611 setup_midi_tab_for_jack ();
614 midi_vbox.pack_start (midi_device_table, true, true);
615 midi_vbox.pack_start (midi_back_button, false, false);
616 midi_vbox.show_all ();
620 EngineControl::setup_midi_tab_for_jack ()
625 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
627 device->input_latency = a->get_value();
629 device->output_latency = a->get_value();
634 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
635 b->set_active (!b->get_active());
636 device->enabled = b->get_active();
637 refresh_midi_display(device->name);
641 EngineControl::refresh_midi_display (std::string focus)
643 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
647 AttachOptions xopt = AttachOptions (FILL|EXPAND);
650 Gtkmm2ext::container_clear (midi_device_table);
652 midi_device_table.set_spacings (6);
654 l = manage (new Label);
655 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
656 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
657 l->set_alignment (0.5, 0.5);
661 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
662 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
663 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
664 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
666 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
667 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
668 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
669 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
672 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
677 bool enabled = (*p)->enabled;
679 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
680 m->set_name ("midi device");
681 m->set_can_focus (Gtk::CAN_FOCUS);
682 m->add_events (Gdk::BUTTON_RELEASE_MASK);
683 m->set_active (enabled);
684 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
685 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
686 if ((*p)->name == focus) {
690 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
691 s = manage (new Gtk::SpinButton (*a));
692 a->set_value ((*p)->input_latency);
693 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
694 s->set_sensitive (_can_set_midi_latencies && enabled);
695 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
697 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
698 s = manage (new Gtk::SpinButton (*a));
699 a->set_value ((*p)->output_latency);
700 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
701 s->set_sensitive (_can_set_midi_latencies && enabled);
702 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
704 b = manage (new Button (_("Calibrate")));
705 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
706 b->set_sensitive (_can_set_midi_latencies && enabled);
707 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
714 EngineControl::update_sensitivity ()
719 EngineControl::backend_changed ()
721 string backend_name = backend_combo.get_active_text();
722 boost::shared_ptr<ARDOUR::AudioBackend> backend;
724 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
725 /* eh? setting the backend failed... how ? */
726 /* A: stale config contains a backend that does not exist in current build */
730 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
733 setup_midi_tab_for_backend ();
734 _midi_devices.clear();
736 if (backend->requires_driver_selection()) {
737 vector<string> drivers = backend->enumerate_drivers();
738 driver_combo.set_sensitive (true);
740 if (!drivers.empty()) {
742 string current_driver;
743 current_driver = backend->driver_name ();
745 // driver might not have been set yet
746 if (current_driver == "") {
747 current_driver = driver_combo.get_active_text ();
748 if (current_driver == "")
749 // driver has never been set, make sure it's not blank
750 current_driver = drivers.front ();
753 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
754 set_popdown_strings (driver_combo, drivers);
755 driver_combo.set_active_text (current_driver);
762 driver_combo.set_sensitive (false);
763 /* this will change the device text which will cause a call to
764 * device changed which will set up parameters
769 vector<string> midi_options = backend->enumerate_midi_options();
771 if (midi_options.size() == 1) {
772 /* only contains the "none" option */
773 midi_option_combo.set_sensitive (false);
776 set_popdown_strings (midi_option_combo, midi_options);
777 midi_option_combo.set_active_text (midi_options.front());
778 midi_option_combo.set_sensitive (true);
780 midi_option_combo.set_sensitive (false);
784 connect_disconnect_button.hide();
786 midi_option_changed();
788 started_at_least_once = false;
790 if (!ignore_changes) {
791 maybe_display_saved_state ();
796 EngineControl::print_channel_count (Gtk::SpinButton* sb)
798 if (ARDOUR::Profile->get_mixbus()) {
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 set_active_text_if_present (buffer_size_combo, 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 %s", sz, P_("sample", "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 ();
1059 EngineControl::buffer_size_changed ()
1061 show_buffer_duration ();
1065 EngineControl::show_buffer_duration ()
1068 /* buffer sizes - convert from just samples to samples + msecs for
1069 * the displayed string
1072 string bs_text = buffer_size_combo.get_active_text ();
1073 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1074 uint32_t rate = get_rate();
1076 /* Developers: note the hard-coding of a double buffered model
1077 in the (2 * samples) computation of latency. we always start
1078 the audiobackend in this configuration.
1080 /* note to jack1 developers: ardour also always starts the engine
1081 * in async mode (no jack2 --sync option) which adds an extra cycle
1082 * of latency with jack2 (and *3 would be correct)
1083 * The value can also be wrong if jackd is started externally..
1085 * At the time of writing the ALSA backend always uses double-buffering *2,
1086 * The Dummy backend *1, and who knows what ASIO really does :)
1088 * So just display the period size, that's also what
1089 * ARDOUR_UI::update_sample_rate() does for the status bar.
1090 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1091 * but still, that's the buffer period, not [round-trip] latency)
1094 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1095 buffer_size_duration_label.set_text (buf);
1099 EngineControl::midi_option_changed ()
1101 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1104 backend->set_midi_option (get_midi_option());
1106 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1108 //_midi_devices.clear(); // TODO merge with state-saved settings..
1109 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1110 std::vector<MidiDeviceSettings> new_devices;
1112 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1113 MidiDeviceSettings mds = find_midi_device (i->name);
1114 if (i->available && !mds) {
1115 uint32_t input_latency = 0;
1116 uint32_t output_latency = 0;
1117 if (_can_set_midi_latencies) {
1118 input_latency = backend->systemic_midi_input_latency (i->name);
1119 output_latency = backend->systemic_midi_output_latency (i->name);
1121 bool enabled = backend->midi_device_enabled (i->name);
1122 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1123 new_devices.push_back (ptr);
1124 } else if (i->available) {
1125 new_devices.push_back (mds);
1128 _midi_devices = new_devices;
1130 if (_midi_devices.empty()) {
1131 midi_devices_button.set_sensitive (false);
1133 midi_devices_button.set_sensitive (true);
1138 EngineControl::parameter_changed ()
1142 EngineControl::State
1143 EngineControl::get_matching_state (
1144 const string& backend,
1145 const string& driver,
1146 const string& device)
1148 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1149 if ((*i)->backend == backend &&
1150 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1158 EngineControl::State
1159 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1161 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1164 return get_matching_state (backend_combo.get_active_text(),
1165 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1166 device_combo.get_active_text());
1170 return get_matching_state (backend_combo.get_active_text(),
1172 device_combo.get_active_text());
1175 EngineControl::State
1176 EngineControl::save_state ()
1180 if (!_have_control) {
1181 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1185 state.reset(new StateStruct);
1186 state->backend = get_backend ();
1188 state.reset(new StateStruct);
1189 store_state (state);
1192 for (StateList::iterator i = states.begin(); i != states.end();) {
1193 if ((*i)->backend == state->backend &&
1194 (*i)->driver == state->driver &&
1195 (*i)->device == state->device) {
1196 i = states.erase(i);
1202 states.push_back (state);
1208 EngineControl::store_state (State state)
1210 state->backend = get_backend ();
1211 state->driver = get_driver ();
1212 state->device = get_device_name ();
1213 state->sample_rate = get_rate ();
1214 state->buffer_size = get_buffer_size ();
1215 state->input_latency = get_input_latency ();
1216 state->output_latency = get_output_latency ();
1217 state->input_channels = get_input_channels ();
1218 state->output_channels = get_output_channels ();
1219 state->midi_option = get_midi_option ();
1220 state->midi_devices = _midi_devices;
1224 EngineControl::maybe_display_saved_state ()
1226 if (!_have_control) {
1230 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1233 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1235 if (!_desired_sample_rate) {
1236 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1238 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1239 /* call this explicitly because we're ignoring changes to
1240 the controls at this point.
1242 show_buffer_duration ();
1243 input_latency.set_value (state->input_latency);
1244 output_latency.set_value (state->output_latency);
1246 if (!state->midi_option.empty()) {
1247 midi_option_combo.set_active_text (state->midi_option);
1248 _midi_devices = state->midi_devices;
1254 EngineControl::get_state ()
1256 XMLNode* root = new XMLNode ("AudioMIDISetup");
1259 if (!states.empty()) {
1260 XMLNode* state_nodes = new XMLNode ("EngineStates");
1262 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1264 XMLNode* node = new XMLNode ("State");
1266 node->add_property ("backend", (*i)->backend);
1267 node->add_property ("driver", (*i)->driver);
1268 node->add_property ("device", (*i)->device);
1269 node->add_property ("sample-rate", (*i)->sample_rate);
1270 node->add_property ("buffer-size", (*i)->buffer_size);
1271 node->add_property ("input-latency", (*i)->input_latency);
1272 node->add_property ("output-latency", (*i)->output_latency);
1273 node->add_property ("input-channels", (*i)->input_channels);
1274 node->add_property ("output-channels", (*i)->output_channels);
1275 node->add_property ("active", (*i)->active ? "yes" : "no");
1276 node->add_property ("midi-option", (*i)->midi_option);
1278 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1279 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1280 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1281 midi_device_stuff->add_property (X_("name"), (*p)->name);
1282 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1283 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1284 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1285 midi_devices->add_child_nocopy (*midi_device_stuff);
1287 node->add_child_nocopy (*midi_devices);
1289 state_nodes->add_child_nocopy (*node);
1292 root->add_child_nocopy (*state_nodes);
1299 EngineControl::set_state (const XMLNode& root)
1301 XMLNodeList clist, cclist;
1302 XMLNodeConstIterator citer, cciter;
1304 XMLNode* grandchild;
1305 XMLProperty* prop = NULL;
1307 if (root.name() != "AudioMIDISetup") {
1311 clist = root.children();
1315 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1319 if (child->name() != "EngineStates") {
1323 cclist = child->children();
1325 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1326 State state (new StateStruct);
1328 grandchild = *cciter;
1330 if (grandchild->name() != "State") {
1334 if ((prop = grandchild->property ("backend")) == 0) {
1337 state->backend = prop->value ();
1339 if ((prop = grandchild->property ("driver")) == 0) {
1342 state->driver = prop->value ();
1344 if ((prop = grandchild->property ("device")) == 0) {
1347 state->device = prop->value ();
1349 if ((prop = grandchild->property ("sample-rate")) == 0) {
1352 state->sample_rate = atof (prop->value ());
1354 if ((prop = grandchild->property ("buffer-size")) == 0) {
1357 state->buffer_size = atoi (prop->value ());
1359 if ((prop = grandchild->property ("input-latency")) == 0) {
1362 state->input_latency = atoi (prop->value ());
1364 if ((prop = grandchild->property ("output-latency")) == 0) {
1367 state->output_latency = atoi (prop->value ());
1369 if ((prop = grandchild->property ("input-channels")) == 0) {
1372 state->input_channels = atoi (prop->value ());
1374 if ((prop = grandchild->property ("output-channels")) == 0) {
1377 state->output_channels = atoi (prop->value ());
1379 if ((prop = grandchild->property ("active")) == 0) {
1382 state->active = string_is_affirmative (prop->value ());
1384 if ((prop = grandchild->property ("midi-option")) == 0) {
1387 state->midi_option = prop->value ();
1389 state->midi_devices.clear();
1391 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1392 const XMLNodeList mnc = midinode->children();
1393 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1394 if ((*n)->property (X_("name")) == 0
1395 || (*n)->property (X_("enabled")) == 0
1396 || (*n)->property (X_("input-latency")) == 0
1397 || (*n)->property (X_("output-latency")) == 0
1402 MidiDeviceSettings ptr (new MidiDeviceSetting(
1403 (*n)->property (X_("name"))->value (),
1404 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1405 atoi ((*n)->property (X_("input-latency"))->value ()),
1406 atoi ((*n)->property (X_("output-latency"))->value ())
1408 state->midi_devices.push_back (ptr);
1413 /* remove accumulated duplicates (due to bug in ealier version)
1414 * this can be removed again before release
1416 for (StateList::iterator i = states.begin(); i != states.end();) {
1417 if ((*i)->backend == state->backend &&
1418 (*i)->driver == state->driver &&
1419 (*i)->device == state->device) {
1420 i = states.erase(i);
1427 states.push_back (state);
1431 /* now see if there was an active state and switch the setup to it */
1433 // purge states of backend that are not available in this built
1434 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1435 vector<std::string> backend_names;
1437 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1438 backend_names.push_back((*i)->name);
1440 for (StateList::iterator i = states.begin(); i != states.end();) {
1441 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1442 i = states.erase(i);
1448 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1451 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1452 backend_combo.set_active_text ((*i)->backend);
1453 driver_combo.set_active_text ((*i)->driver);
1454 device_combo.set_active_text ((*i)->device);
1455 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1456 set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
1457 input_latency.set_value ((*i)->input_latency);
1458 output_latency.set_value ((*i)->output_latency);
1459 midi_option_combo.set_active_text ((*i)->midi_option);
1466 EngineControl::push_state_to_backend (bool start)
1468 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1474 /* figure out what is going to change */
1476 bool restart_required = false;
1477 bool was_running = ARDOUR::AudioEngine::instance()->running();
1478 bool change_driver = false;
1479 bool change_device = false;
1480 bool change_rate = false;
1481 bool change_bufsize = false;
1482 bool change_latency = false;
1483 bool change_channels = false;
1484 bool change_midi = false;
1486 uint32_t ochan = get_output_channels ();
1487 uint32_t ichan = get_input_channels ();
1489 if (_have_control) {
1491 if (started_at_least_once) {
1493 /* we can control the backend */
1495 if (backend->requires_driver_selection()) {
1496 if (get_driver() != backend->driver_name()) {
1497 change_driver = true;
1501 if (queue_device_changed || get_device_name() != backend->device_name()) {
1502 change_device = true;
1505 if (get_rate() != backend->sample_rate()) {
1509 if (get_buffer_size() != backend->buffer_size()) {
1510 change_bufsize = true;
1513 if (get_midi_option() != backend->midi_option()) {
1517 /* zero-requested channels means "all available" */
1520 ichan = backend->input_channels();
1524 ochan = backend->output_channels();
1527 if (ichan != backend->input_channels()) {
1528 change_channels = true;
1531 if (ochan != backend->output_channels()) {
1532 change_channels = true;
1535 if (get_input_latency() != backend->systemic_input_latency() ||
1536 get_output_latency() != backend->systemic_output_latency()) {
1537 change_latency = true;
1540 /* backend never started, so we have to force a group
1543 change_device = true;
1544 if (backend->requires_driver_selection()) {
1545 change_driver = true;
1548 change_bufsize = true;
1549 change_channels = true;
1550 change_latency = true;
1556 /* we have no control over the backend, meaning that we can
1557 * only possibly change sample rate and buffer size.
1561 if (get_rate() != backend->sample_rate()) {
1562 change_bufsize = true;
1565 if (get_buffer_size() != backend->buffer_size()) {
1566 change_bufsize = true;
1570 queue_device_changed = false;
1572 if (!_have_control) {
1574 /* We do not have control over the backend, so the best we can
1575 * do is try to change the sample rate and/or bufsize and get
1579 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1583 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1588 backend->set_sample_rate (get_rate());
1591 if (change_bufsize) {
1592 backend->set_buffer_size (get_buffer_size());
1596 if (ARDOUR::AudioEngine::instance()->start ()) {
1597 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1607 /* determine if we need to stop the backend before changing parameters */
1609 if (change_driver || change_device || change_channels || change_latency ||
1610 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1612 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1613 restart_required = true;
1615 restart_required = false;
1620 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1621 /* no changes in any parameters that absolutely require a
1622 * restart, so check those that might be changeable without a
1626 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1627 /* can't do this while running ... */
1628 restart_required = true;
1631 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1632 /* can't do this while running ... */
1633 restart_required = true;
1639 if (restart_required) {
1640 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1647 if (change_driver && backend->set_driver (get_driver())) {
1648 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1651 if (change_device && backend->set_device_name (get_device_name())) {
1652 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1655 if (change_rate && backend->set_sample_rate (get_rate())) {
1656 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1659 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1660 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1664 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1665 if (backend->set_input_channels (get_input_channels())) {
1666 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1669 if (backend->set_output_channels (get_output_channels())) {
1670 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1674 if (change_latency) {
1675 if (backend->set_systemic_input_latency (get_input_latency())) {
1676 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1679 if (backend->set_systemic_output_latency (get_output_latency())) {
1680 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1686 backend->set_midi_option (get_midi_option());
1690 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1691 if (_measure_midi) {
1692 if (*p == _measure_midi) {
1693 backend->set_midi_device_enabled ((*p)->name, true);
1695 backend->set_midi_device_enabled ((*p)->name, false);
1699 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1700 if (backend->can_set_systemic_midi_latencies()) {
1701 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1702 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1707 if (start || (was_running && restart_required)) {
1708 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1719 EngineControl::post_push ()
1721 /* get a pointer to the current state object, creating one if
1725 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1728 state = save_state ();
1736 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1737 (*i)->active = false;
1740 /* mark this one active (to be used next time the dialog is
1744 state->active = true;
1746 if (_have_control) { // XXX
1747 manage_control_app_sensitivity ();
1750 /* schedule a redisplay of MIDI ports */
1751 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1756 EngineControl::get_rate () const
1758 float r = atof (sample_rate_combo.get_active_text ());
1759 /* the string may have been translated with an abbreviation for
1760 * thousands, so use a crude heuristic to fix this.
1770 EngineControl::get_buffer_size () const
1772 string txt = buffer_size_combo.get_active_text ();
1775 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1776 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
1777 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
1785 EngineControl::get_midi_option () const
1787 return midi_option_combo.get_active_text();
1791 EngineControl::get_input_channels() const
1793 if (ARDOUR::Profile->get_mixbus()) {
1794 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1795 if (!backend) return 0;
1796 return backend->input_channels();
1798 return (uint32_t) input_channels_adjustment.get_value();
1802 EngineControl::get_output_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) output_channels_adjustment.get_value();
1813 EngineControl::get_input_latency() const
1815 return (uint32_t) input_latency_adjustment.get_value();
1819 EngineControl::get_output_latency() const
1821 return (uint32_t) output_latency_adjustment.get_value();
1825 EngineControl::get_backend () const
1827 return backend_combo.get_active_text ();
1831 EngineControl::get_driver () const
1833 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1834 return driver_combo.get_active_text ();
1841 EngineControl::get_device_name () const
1843 return device_combo.get_active_text ();
1847 EngineControl::control_app_button_clicked ()
1849 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1855 backend->launch_control_app ();
1859 EngineControl::manage_control_app_sensitivity ()
1861 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1867 string appname = backend->control_app_name();
1869 if (appname.empty()) {
1870 control_app_button.set_sensitive (false);
1872 control_app_button.set_sensitive (true);
1877 EngineControl::set_desired_sample_rate (uint32_t sr)
1879 _desired_sample_rate = sr;
1884 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1886 if (page_num == 0) {
1887 cancel_button->set_sensitive (true);
1888 ok_button->set_sensitive (true);
1889 apply_button->set_sensitive (true);
1890 _measure_midi.reset();
1892 cancel_button->set_sensitive (false);
1893 ok_button->set_sensitive (false);
1894 apply_button->set_sensitive (false);
1897 if (page_num == midi_tab) {
1899 refresh_midi_display ();
1902 if (page_num == latency_tab) {
1905 if (ARDOUR::AudioEngine::instance()->running()) {
1906 // TODO - mark as 'stopped for latency
1907 ARDOUR_UI::instance()->disconnect_from_engine ();
1911 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1913 /* save any existing latency values */
1915 uint32_t il = (uint32_t) input_latency.get_value ();
1916 uint32_t ol = (uint32_t) input_latency.get_value ();
1918 /* reset to zero so that our new test instance
1919 will be clean of any existing latency measures.
1921 NB. this should really be done by the backend
1922 when stated for latency measurement.
1925 input_latency.set_value (0);
1926 output_latency.set_value (0);
1928 push_state_to_backend (false);
1932 input_latency.set_value (il);
1933 output_latency.set_value (ol);
1936 // This should be done in push_state_to_backend()
1937 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1938 disable_latency_tab ();
1941 enable_latency_tab ();
1945 end_latency_detection ();
1946 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1951 /* latency measurement */
1954 EngineControl::check_audio_latency_measurement ()
1956 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1958 if (mtdm->resolve () < 0) {
1959 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1963 if (mtdm->err () > 0.3) {
1969 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1971 if (sample_rate == 0) {
1972 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1973 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1977 int frames_total = mtdm->del();
1978 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1980 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1981 _("Detected roundtrip latency: "),
1982 frames_total, frames_total * 1000.0f/sample_rate,
1983 _("Systemic latency: "),
1984 extra, extra * 1000.0f/sample_rate);
1988 if (mtdm->err () > 0.2) {
1990 strcat (buf, _("(signal detection error)"));
1996 strcat (buf, _("(inverted - bad wiring)"));
2000 lm_results.set_markup (string_compose (results_markup, buf));
2003 have_lm_results = true;
2004 end_latency_detection ();
2005 lm_use_button.set_sensitive (true);
2013 EngineControl::check_midi_latency_measurement ()
2015 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2017 if (!mididm->have_signal () || mididm->latency () == 0) {
2018 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2023 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2025 if (sample_rate == 0) {
2026 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2027 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2031 ARDOUR::framecnt_t frames_total = mididm->latency();
2032 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2033 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2034 _("Detected roundtrip latency: "),
2035 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2036 _("Systemic latency: "),
2037 extra, extra * 1000.0f / sample_rate);
2041 if (!mididm->ok ()) {
2043 strcat (buf, _("(averaging)"));
2047 if (mididm->deviation () > 50.0) {
2049 strcat (buf, _("(too large jitter)"));
2051 } else if (mididm->deviation () > 10.0) {
2053 strcat (buf, _("(large jitter)"));
2057 have_lm_results = true;
2058 end_latency_detection ();
2059 lm_use_button.set_sensitive (true);
2060 lm_results.set_markup (string_compose (results_markup, buf));
2062 } else if (mididm->processed () > 400) {
2063 have_lm_results = false;
2064 end_latency_detection ();
2065 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2069 lm_results.set_markup (string_compose (results_markup, buf));
2075 EngineControl::start_latency_detection ()
2077 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2078 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2080 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2081 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2082 if (_measure_midi) {
2083 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2085 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2087 lm_measure_label.set_text (_("Cancel"));
2088 have_lm_results = false;
2089 lm_use_button.set_sensitive (false);
2090 lm_input_channel_combo.set_sensitive (false);
2091 lm_output_channel_combo.set_sensitive (false);
2097 EngineControl::end_latency_detection ()
2099 latency_timeout.disconnect ();
2100 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2101 lm_measure_label.set_text (_("Measure"));
2102 if (!have_lm_results) {
2103 lm_use_button.set_sensitive (false);
2105 lm_input_channel_combo.set_sensitive (true);
2106 lm_output_channel_combo.set_sensitive (true);
2111 EngineControl::latency_button_clicked ()
2114 start_latency_detection ();
2116 end_latency_detection ();
2121 EngineControl::use_latency_button_clicked ()
2123 if (_measure_midi) {
2124 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2128 ARDOUR::framecnt_t frames_total = mididm->latency();
2129 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2130 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2131 _measure_midi->input_latency = one_way;
2132 _measure_midi->output_latency = one_way;
2133 notebook.set_current_page (midi_tab);
2135 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2141 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2142 one_way = std::max (0., one_way);
2144 input_latency_adjustment.set_value (one_way);
2145 output_latency_adjustment.set_value (one_way);
2147 /* back to settings page */
2148 notebook.set_current_page (0);
2154 EngineControl::on_delete_event (GdkEventAny* ev)
2156 if (notebook.get_current_page() == 2) {
2157 /* currently on latency tab - be sure to clean up */
2158 end_latency_detection ();
2160 return ArdourDialog::on_delete_event (ev);
2164 EngineControl::engine_running ()
2166 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2169 set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2170 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2172 buffer_size_combo.set_sensitive (true);
2173 sample_rate_combo.set_sensitive (true);
2175 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2176 connect_disconnect_button.show();
2178 started_at_least_once = true;
2179 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2183 EngineControl::engine_stopped ()
2185 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2188 buffer_size_combo.set_sensitive (false);
2189 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2190 connect_disconnect_button.show();
2192 sample_rate_combo.set_sensitive (true);
2193 buffer_size_combo.set_sensitive (true);
2194 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2198 EngineControl::device_list_changed ()
2200 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1); // ??
2202 midi_option_changed();
2206 EngineControl::connect_disconnect_click()
2208 if (ARDOUR::AudioEngine::instance()->running()) {
2209 ARDOUR_UI::instance()->disconnect_from_engine ();
2211 ARDOUR_UI::instance()->reconnect_to_engine ();
2216 EngineControl::calibrate_audio_latency ()
2218 _measure_midi.reset ();
2219 have_lm_results = false;
2220 lm_use_button.set_sensitive (false);
2221 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2222 notebook.set_current_page (latency_tab);
2226 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
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::configure_midi_devices ()
2238 notebook.set_current_page (midi_tab);