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 ();
329 ok_button->grab_focus();
333 EngineControl::on_response (int response_id)
335 ArdourDialog::on_response (response_id);
337 switch (response_id) {
339 push_state_to_backend (true);
342 push_state_to_backend (true);
345 case RESPONSE_DELETE_EVENT:
348 ev.type = GDK_BUTTON_PRESS;
350 on_delete_event ((GdkEventAny*) &ev);
359 EngineControl::build_notebook ()
362 AttachOptions xopt = AttachOptions (FILL|EXPAND);
364 /* clear the table */
366 Gtkmm2ext::container_clear (basic_vbox);
367 Gtkmm2ext::container_clear (basic_packer);
369 if (control_app_button.get_parent()) {
370 control_app_button.get_parent()->remove (control_app_button);
373 label = manage (left_aligned_label (_("Audio System:")));
374 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
375 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
377 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
378 lm_button_audio.set_name ("generic button");
379 lm_button_audio.set_can_focus(true);
382 build_full_control_notebook ();
384 build_no_control_notebook ();
387 basic_vbox.pack_start (basic_hbox, false, false);
390 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
391 basic_vbox.show_all ();
396 EngineControl::build_full_control_notebook ()
398 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
401 using namespace Notebook_Helpers;
403 vector<string> strings;
404 AttachOptions xopt = AttachOptions (FILL|EXPAND);
405 int row = 1; // row zero == backend combo
407 /* start packing it up */
409 if (backend->requires_driver_selection()) {
410 label = manage (left_aligned_label (_("Driver:")));
411 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
412 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
416 label = manage (left_aligned_label (_("Device:")));
417 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
418 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
421 label = manage (left_aligned_label (_("Sample rate:")));
422 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
423 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
427 label = manage (left_aligned_label (_("Buffer size:")));
428 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
429 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
430 buffer_size_duration_label.set_alignment (0.0); /* left-align */
431 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
433 /* button spans 2 rows */
435 basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
438 input_channels.set_name ("InputChannels");
439 input_channels.set_flags (Gtk::CAN_FOCUS);
440 input_channels.set_digits (0);
441 input_channels.set_wrap (false);
442 output_channels.set_editable (true);
444 if (!ARDOUR::Profile->get_mixbus()) {
445 label = manage (left_aligned_label (_("Input Channels:")));
446 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
447 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
451 output_channels.set_name ("OutputChannels");
452 output_channels.set_flags (Gtk::CAN_FOCUS);
453 output_channels.set_digits (0);
454 output_channels.set_wrap (false);
455 output_channels.set_editable (true);
457 if (!ARDOUR::Profile->get_mixbus()) {
458 label = manage (left_aligned_label (_("Output Channels:")));
459 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
460 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
464 input_latency.set_name ("InputLatency");
465 input_latency.set_flags (Gtk::CAN_FOCUS);
466 input_latency.set_digits (0);
467 input_latency.set_wrap (false);
468 input_latency.set_editable (true);
470 label = manage (left_aligned_label (_("Hardware input latency:")));
471 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
472 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
473 label = manage (left_aligned_label (_("samples")));
474 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
477 output_latency.set_name ("OutputLatency");
478 output_latency.set_flags (Gtk::CAN_FOCUS);
479 output_latency.set_digits (0);
480 output_latency.set_wrap (false);
481 output_latency.set_editable (true);
483 label = manage (left_aligned_label (_("Hardware output latency:")));
484 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
485 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
486 label = manage (left_aligned_label (_("samples")));
487 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
489 /* button spans 2 rows */
491 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
494 label = manage (left_aligned_label (_("MIDI System:")));
495 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
496 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
497 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
502 EngineControl::build_no_control_notebook ()
504 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
507 using namespace Notebook_Helpers;
509 vector<string> strings;
510 AttachOptions xopt = AttachOptions (FILL|EXPAND);
511 int row = 1; // row zero == backend combo
512 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
514 label = manage (new Label);
515 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
516 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
519 if (backend->can_change_sample_rate_when_running()) {
520 label = manage (left_aligned_label (_("Sample rate:")));
521 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
522 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
526 if (backend->can_change_buffer_size_when_running()) {
527 label = manage (left_aligned_label (_("Buffer size:")));
528 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
529 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
530 buffer_size_duration_label.set_alignment (0.0); /* left-align */
531 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
535 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
539 EngineControl::~EngineControl ()
541 ignore_changes = true;
545 EngineControl::disable_latency_tab ()
547 vector<string> empty;
548 set_popdown_strings (lm_output_channel_combo, empty);
549 set_popdown_strings (lm_input_channel_combo, empty);
550 lm_measure_button.set_sensitive (false);
551 lm_use_button.set_sensitive (false);
555 EngineControl::enable_latency_tab ()
557 vector<string> outputs;
558 vector<string> inputs;
560 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
561 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
562 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
564 if (!ARDOUR::AudioEngine::instance()->running()) {
565 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
566 notebook.set_current_page (0);
570 else if (inputs.empty() || outputs.empty()) {
571 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
572 notebook.set_current_page (0);
577 lm_back_button_signal.disconnect();
579 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
582 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
586 set_popdown_strings (lm_output_channel_combo, outputs);
587 lm_output_channel_combo.set_active_text (outputs.front());
588 lm_output_channel_combo.set_sensitive (true);
590 set_popdown_strings (lm_input_channel_combo, inputs);
591 lm_input_channel_combo.set_active_text (inputs.front());
592 lm_input_channel_combo.set_sensitive (true);
594 lm_measure_button.set_sensitive (true);
598 EngineControl::setup_midi_tab_for_backend ()
600 string backend = backend_combo.get_active_text ();
602 Gtkmm2ext::container_clear (midi_vbox);
604 midi_vbox.set_border_width (12);
605 midi_device_table.set_border_width (12);
607 if (backend == "JACK") {
608 setup_midi_tab_for_jack ();
611 midi_vbox.pack_start (midi_device_table, true, true);
612 midi_vbox.pack_start (midi_back_button, false, false);
613 midi_vbox.show_all ();
617 EngineControl::setup_midi_tab_for_jack ()
622 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
624 device->input_latency = a->get_value();
626 device->output_latency = a->get_value();
631 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
632 b->set_active (!b->get_active());
633 device->enabled = b->get_active();
634 refresh_midi_display(device->name);
638 EngineControl::refresh_midi_display (std::string focus)
640 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
644 AttachOptions xopt = AttachOptions (FILL|EXPAND);
647 Gtkmm2ext::container_clear (midi_device_table);
649 midi_device_table.set_spacings (6);
651 l = manage (new Label);
652 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
653 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
654 l->set_alignment (0.5, 0.5);
658 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
659 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
660 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
661 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
663 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
664 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
665 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
666 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
669 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
674 bool enabled = (*p)->enabled;
676 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
677 m->set_name ("midi device");
678 m->set_can_focus (Gtk::CAN_FOCUS);
679 m->add_events (Gdk::BUTTON_RELEASE_MASK);
680 m->set_active (enabled);
681 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
682 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
683 if ((*p)->name == focus) {
687 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
688 s = manage (new Gtk::SpinButton (*a));
689 a->set_value ((*p)->input_latency);
690 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
691 s->set_sensitive (_can_set_midi_latencies && enabled);
692 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
694 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
695 s = manage (new Gtk::SpinButton (*a));
696 a->set_value ((*p)->output_latency);
697 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
698 s->set_sensitive (_can_set_midi_latencies && enabled);
699 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
701 b = manage (new Button (_("Calibrate")));
702 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
703 b->set_sensitive (_can_set_midi_latencies && enabled);
704 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
711 EngineControl::update_sensitivity ()
716 EngineControl::backend_changed ()
718 string backend_name = backend_combo.get_active_text();
719 boost::shared_ptr<ARDOUR::AudioBackend> backend;
721 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
722 /* eh? setting the backend failed... how ? */
723 /* A: stale config contains a backend that does not exist in current build */
727 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
730 setup_midi_tab_for_backend ();
731 _midi_devices.clear();
733 if (backend->requires_driver_selection()) {
734 vector<string> drivers = backend->enumerate_drivers();
735 driver_combo.set_sensitive (true);
737 if (!drivers.empty()) {
739 string current_driver;
740 current_driver = backend->driver_name ();
742 // driver might not have been set yet
743 if (current_driver == "") {
744 current_driver = driver_combo.get_active_text ();
745 if (current_driver == "")
746 // driver has never been set, make sure it's not blank
747 current_driver = drivers.front ();
750 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
751 set_popdown_strings (driver_combo, drivers);
752 driver_combo.set_active_text (current_driver);
759 driver_combo.set_sensitive (false);
760 /* this will change the device text which will cause a call to
761 * device changed which will set up parameters
766 vector<string> midi_options = backend->enumerate_midi_options();
768 if (midi_options.size() == 1) {
769 /* only contains the "none" option */
770 midi_option_combo.set_sensitive (false);
773 set_popdown_strings (midi_option_combo, midi_options);
774 midi_option_combo.set_active_text (midi_options.front());
775 midi_option_combo.set_sensitive (true);
777 midi_option_combo.set_sensitive (false);
781 connect_disconnect_button.hide();
783 midi_option_changed();
785 started_at_least_once = false;
787 if (!ignore_changes) {
788 maybe_display_saved_state ();
793 EngineControl::print_channel_count (Gtk::SpinButton* sb)
795 if (ARDOUR::Profile->get_mixbus()) {
799 uint32_t cnt = (uint32_t) sb->get_value();
801 sb->set_text (_("all available channels"));
804 snprintf (buf, sizeof (buf), "%d", cnt);
811 EngineControl::list_devices ()
813 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
816 /* now fill out devices, mark sample rates, buffer sizes insensitive */
818 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
820 /* NOTE: Ardour currently does not display the "available" field of the
823 * Doing so would require a different GUI widget than the combo
824 * box/popdown that we currently use, since it has no way to list
825 * items that are not selectable. Something more like a popup menu,
826 * which could have unselectable items, would be appropriate.
829 vector<string> available_devices;
831 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
832 available_devices.push_back (i->name);
835 if (!available_devices.empty()) {
837 update_sensitivity ();
840 string current_device, found_device;
841 current_device = device_combo.get_active_text ();
842 if (current_device == "") {
843 current_device = backend->device_name ();
846 // Make sure that the active text is still relevant for this
847 // device (it might only be relevant to the previous device!!)
848 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
849 if (*i == current_device)
850 found_device = current_device;
852 if (found_device == "")
853 // device has never been set (or was not relevant
854 // for this backend) Let's make sure it's not blank
855 current_device = available_devices.front ();
857 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
858 set_popdown_strings (device_combo, available_devices);
860 device_combo.set_active_text (current_device);
865 input_latency.set_sensitive (true);
866 output_latency.set_sensitive (true);
867 input_channels.set_sensitive (true);
868 output_channels.set_sensitive (true);
870 ok_button->set_sensitive (true);
871 apply_button->set_sensitive (true);
874 device_combo.clear();
875 sample_rate_combo.set_sensitive (false);
876 buffer_size_combo.set_sensitive (false);
877 input_latency.set_sensitive (false);
878 output_latency.set_sensitive (false);
879 input_channels.set_sensitive (false);
880 output_channels.set_sensitive (false);
882 ok_button->set_sensitive (false);
883 apply_button->set_sensitive (false);
885 ok_button->set_sensitive (true);
886 apply_button->set_sensitive (true);
887 if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
888 sample_rate_combo.set_sensitive (true);
890 if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
891 buffer_size_combo.set_sensitive (true);
899 EngineControl::driver_changed ()
901 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
904 backend->set_driver (driver_combo.get_active_text());
907 if (!ignore_changes) {
908 maybe_display_saved_state ();
913 EngineControl::device_changed ()
916 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
918 string device_name = device_combo.get_active_text ();
921 if (device_name != backend->device_name()) {
922 /* we set the backend-device to query various device related intormation.
923 * This has the side effect that backend->device_name() will match
924 * the device_name and 'change_device' will never be true.
925 * so work around this by setting...
927 queue_device_changed = true;
930 //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
931 backend->set_device_name(device_name);
934 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
936 /* don't allow programmatic change to combos to cause a
937 recursive call to this method.
947 sr = backend->available_sample_rates (device_name);
950 sr.push_back (8000.0f);
951 sr.push_back (16000.0f);
952 sr.push_back (32000.0f);
953 sr.push_back (44100.0f);
954 sr.push_back (48000.0f);
955 sr.push_back (88200.0f);
956 sr.push_back (96000.0f);
957 sr.push_back (192000.0f);
958 sr.push_back (384000.0f);
961 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
962 s.push_back (rate_as_string (*x));
963 if (*x == _desired_sample_rate) {
969 sample_rate_combo.set_sensitive (true);
970 set_popdown_strings (sample_rate_combo, s);
972 if (desired.empty()) {
973 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
975 sample_rate_combo.set_active_text (desired);
979 sample_rate_combo.set_sensitive (false);
987 bs = backend->available_buffer_sizes (device_name);
988 } else if (backend->can_change_buffer_size_when_running()) {
1002 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1003 s.push_back (bufsize_as_string (*x));
1007 buffer_size_combo.set_sensitive (true);
1008 set_popdown_strings (buffer_size_combo, s);
1010 uint32_t period = backend->buffer_size();
1012 period = backend->default_buffer_size(device_name);
1014 set_active_text_if_present (buffer_size_combo, bufsize_as_string (period));
1015 show_buffer_duration ();
1017 buffer_size_combo.set_sensitive (false);
1020 /* XXX theoretically need to set min + max channel counts here
1023 manage_control_app_sensitivity ();
1026 /* pick up any saved state for this device */
1028 if (!ignore_changes) {
1029 maybe_display_saved_state ();
1034 EngineControl::bufsize_as_string (uint32_t sz)
1036 /* Translators: "samples" is always plural here, so no
1037 need for plural+singular forms.
1040 snprintf (buf, sizeof (buf), "%u %s", sz, P_("sample", "samples", sz));
1045 EngineControl::sample_rate_changed ()
1047 /* reset the strings for buffer size to show the correct msec value
1048 (reflecting the new sample rate).
1051 show_buffer_duration ();
1056 EngineControl::buffer_size_changed ()
1058 show_buffer_duration ();
1062 EngineControl::show_buffer_duration ()
1065 /* buffer sizes - convert from just samples to samples + msecs for
1066 * the displayed string
1069 string bs_text = buffer_size_combo.get_active_text ();
1070 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1071 uint32_t rate = get_rate();
1073 /* Developers: note the hard-coding of a double buffered model
1074 in the (2 * samples) computation of latency. we always start
1075 the audiobackend in this configuration.
1077 /* note to jack1 developers: ardour also always starts the engine
1078 * in async mode (no jack2 --sync option) which adds an extra cycle
1079 * of latency with jack2 (and *3 would be correct)
1080 * The value can also be wrong if jackd is started externally..
1082 * At the time of writing the ALSA backend always uses double-buffering *2,
1083 * The Dummy backend *1, and who knows what ASIO really does :)
1085 * So just display the period size, that's also what
1086 * ARDOUR_UI::update_sample_rate() does for the status bar.
1087 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1088 * but still, that's the buffer period, not [round-trip] latency)
1091 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1092 buffer_size_duration_label.set_text (buf);
1096 EngineControl::midi_option_changed ()
1098 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1101 backend->set_midi_option (get_midi_option());
1103 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1105 //_midi_devices.clear(); // TODO merge with state-saved settings..
1106 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1107 std::vector<MidiDeviceSettings> new_devices;
1109 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1110 MidiDeviceSettings mds = find_midi_device (i->name);
1111 if (i->available && !mds) {
1112 uint32_t input_latency = 0;
1113 uint32_t output_latency = 0;
1114 if (_can_set_midi_latencies) {
1115 input_latency = backend->systemic_midi_input_latency (i->name);
1116 output_latency = backend->systemic_midi_output_latency (i->name);
1118 bool enabled = backend->midi_device_enabled (i->name);
1119 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1120 new_devices.push_back (ptr);
1121 } else if (i->available) {
1122 new_devices.push_back (mds);
1125 _midi_devices = new_devices;
1127 if (_midi_devices.empty()) {
1128 midi_devices_button.set_sensitive (false);
1130 midi_devices_button.set_sensitive (true);
1135 EngineControl::parameter_changed ()
1139 EngineControl::State
1140 EngineControl::get_matching_state (
1141 const string& backend,
1142 const string& driver,
1143 const string& device)
1145 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1146 if ((*i)->backend == backend &&
1147 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1155 EngineControl::State
1156 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1158 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1161 return get_matching_state (backend_combo.get_active_text(),
1162 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1163 device_combo.get_active_text());
1167 return get_matching_state (backend_combo.get_active_text(),
1169 device_combo.get_active_text());
1172 EngineControl::State
1173 EngineControl::save_state ()
1177 if (!_have_control) {
1178 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1182 state.reset(new StateStruct);
1183 state->backend = get_backend ();
1185 state.reset(new StateStruct);
1186 store_state (state);
1189 for (StateList::iterator i = states.begin(); i != states.end();) {
1190 if ((*i)->backend == state->backend &&
1191 (*i)->driver == state->driver &&
1192 (*i)->device == state->device) {
1193 i = states.erase(i);
1199 states.push_back (state);
1205 EngineControl::store_state (State state)
1207 state->backend = get_backend ();
1208 state->driver = get_driver ();
1209 state->device = get_device_name ();
1210 state->sample_rate = get_rate ();
1211 state->buffer_size = get_buffer_size ();
1212 state->input_latency = get_input_latency ();
1213 state->output_latency = get_output_latency ();
1214 state->input_channels = get_input_channels ();
1215 state->output_channels = get_output_channels ();
1216 state->midi_option = get_midi_option ();
1217 state->midi_devices = _midi_devices;
1221 EngineControl::maybe_display_saved_state ()
1223 if (!_have_control) {
1227 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1230 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1232 if (!_desired_sample_rate) {
1233 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1235 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1236 /* call this explicitly because we're ignoring changes to
1237 the controls at this point.
1239 show_buffer_duration ();
1240 input_latency.set_value (state->input_latency);
1241 output_latency.set_value (state->output_latency);
1243 if (!state->midi_option.empty()) {
1244 midi_option_combo.set_active_text (state->midi_option);
1245 _midi_devices = state->midi_devices;
1251 EngineControl::get_state ()
1253 XMLNode* root = new XMLNode ("AudioMIDISetup");
1256 if (!states.empty()) {
1257 XMLNode* state_nodes = new XMLNode ("EngineStates");
1259 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1261 XMLNode* node = new XMLNode ("State");
1263 node->add_property ("backend", (*i)->backend);
1264 node->add_property ("driver", (*i)->driver);
1265 node->add_property ("device", (*i)->device);
1266 node->add_property ("sample-rate", (*i)->sample_rate);
1267 node->add_property ("buffer-size", (*i)->buffer_size);
1268 node->add_property ("input-latency", (*i)->input_latency);
1269 node->add_property ("output-latency", (*i)->output_latency);
1270 node->add_property ("input-channels", (*i)->input_channels);
1271 node->add_property ("output-channels", (*i)->output_channels);
1272 node->add_property ("active", (*i)->active ? "yes" : "no");
1273 node->add_property ("midi-option", (*i)->midi_option);
1275 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1276 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1277 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1278 midi_device_stuff->add_property (X_("name"), (*p)->name);
1279 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1280 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1281 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1282 midi_devices->add_child_nocopy (*midi_device_stuff);
1284 node->add_child_nocopy (*midi_devices);
1286 state_nodes->add_child_nocopy (*node);
1289 root->add_child_nocopy (*state_nodes);
1296 EngineControl::set_state (const XMLNode& root)
1298 XMLNodeList clist, cclist;
1299 XMLNodeConstIterator citer, cciter;
1301 XMLNode* grandchild;
1302 XMLProperty* prop = NULL;
1304 if (root.name() != "AudioMIDISetup") {
1308 clist = root.children();
1312 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1316 if (child->name() != "EngineStates") {
1320 cclist = child->children();
1322 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1323 State state (new StateStruct);
1325 grandchild = *cciter;
1327 if (grandchild->name() != "State") {
1331 if ((prop = grandchild->property ("backend")) == 0) {
1334 state->backend = prop->value ();
1336 if ((prop = grandchild->property ("driver")) == 0) {
1339 state->driver = prop->value ();
1341 if ((prop = grandchild->property ("device")) == 0) {
1344 state->device = prop->value ();
1346 if ((prop = grandchild->property ("sample-rate")) == 0) {
1349 state->sample_rate = atof (prop->value ());
1351 if ((prop = grandchild->property ("buffer-size")) == 0) {
1354 state->buffer_size = atoi (prop->value ());
1356 if ((prop = grandchild->property ("input-latency")) == 0) {
1359 state->input_latency = atoi (prop->value ());
1361 if ((prop = grandchild->property ("output-latency")) == 0) {
1364 state->output_latency = atoi (prop->value ());
1366 if ((prop = grandchild->property ("input-channels")) == 0) {
1369 state->input_channels = atoi (prop->value ());
1371 if ((prop = grandchild->property ("output-channels")) == 0) {
1374 state->output_channels = atoi (prop->value ());
1376 if ((prop = grandchild->property ("active")) == 0) {
1379 state->active = string_is_affirmative (prop->value ());
1381 if ((prop = grandchild->property ("midi-option")) == 0) {
1384 state->midi_option = prop->value ();
1386 state->midi_devices.clear();
1388 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1389 const XMLNodeList mnc = midinode->children();
1390 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1391 if ((*n)->property (X_("name")) == 0
1392 || (*n)->property (X_("enabled")) == 0
1393 || (*n)->property (X_("input-latency")) == 0
1394 || (*n)->property (X_("output-latency")) == 0
1399 MidiDeviceSettings ptr (new MidiDeviceSetting(
1400 (*n)->property (X_("name"))->value (),
1401 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1402 atoi ((*n)->property (X_("input-latency"))->value ()),
1403 atoi ((*n)->property (X_("output-latency"))->value ())
1405 state->midi_devices.push_back (ptr);
1410 /* remove accumulated duplicates (due to bug in ealier version)
1411 * this can be removed again before release
1413 for (StateList::iterator i = states.begin(); i != states.end();) {
1414 if ((*i)->backend == state->backend &&
1415 (*i)->driver == state->driver &&
1416 (*i)->device == state->device) {
1417 i = states.erase(i);
1424 states.push_back (state);
1428 /* now see if there was an active state and switch the setup to it */
1430 // purge states of backend that are not available in this built
1431 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1432 vector<std::string> backend_names;
1434 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1435 backend_names.push_back((*i)->name);
1437 for (StateList::iterator i = states.begin(); i != states.end();) {
1438 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1439 i = states.erase(i);
1445 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1448 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1449 backend_combo.set_active_text ((*i)->backend);
1450 driver_combo.set_active_text ((*i)->driver);
1451 device_combo.set_active_text ((*i)->device);
1452 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1453 set_active_text_if_present (buffer_size_combo, bufsize_as_string ((*i)->buffer_size));
1454 input_latency.set_value ((*i)->input_latency);
1455 output_latency.set_value ((*i)->output_latency);
1456 midi_option_combo.set_active_text ((*i)->midi_option);
1463 EngineControl::push_state_to_backend (bool start)
1465 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1471 /* figure out what is going to change */
1473 bool restart_required = false;
1474 bool was_running = ARDOUR::AudioEngine::instance()->running();
1475 bool change_driver = false;
1476 bool change_device = false;
1477 bool change_rate = false;
1478 bool change_bufsize = false;
1479 bool change_latency = false;
1480 bool change_channels = false;
1481 bool change_midi = false;
1483 uint32_t ochan = get_output_channels ();
1484 uint32_t ichan = get_input_channels ();
1486 if (_have_control) {
1488 if (started_at_least_once) {
1490 /* we can control the backend */
1492 if (backend->requires_driver_selection()) {
1493 if (get_driver() != backend->driver_name()) {
1494 change_driver = true;
1498 if (queue_device_changed || get_device_name() != backend->device_name()) {
1499 change_device = true;
1502 if (get_rate() != backend->sample_rate()) {
1506 if (get_buffer_size() != backend->buffer_size()) {
1507 change_bufsize = true;
1510 if (get_midi_option() != backend->midi_option()) {
1514 /* zero-requested channels means "all available" */
1517 ichan = backend->input_channels();
1521 ochan = backend->output_channels();
1524 if (ichan != backend->input_channels()) {
1525 change_channels = true;
1528 if (ochan != backend->output_channels()) {
1529 change_channels = true;
1532 if (get_input_latency() != backend->systemic_input_latency() ||
1533 get_output_latency() != backend->systemic_output_latency()) {
1534 change_latency = true;
1537 /* backend never started, so we have to force a group
1540 change_device = true;
1541 if (backend->requires_driver_selection()) {
1542 change_driver = true;
1545 change_bufsize = true;
1546 change_channels = true;
1547 change_latency = true;
1553 /* we have no control over the backend, meaning that we can
1554 * only possibly change sample rate and buffer size.
1558 if (get_rate() != backend->sample_rate()) {
1559 change_bufsize = true;
1562 if (get_buffer_size() != backend->buffer_size()) {
1563 change_bufsize = true;
1567 queue_device_changed = false;
1569 if (!_have_control) {
1571 /* We do not have control over the backend, so the best we can
1572 * do is try to change the sample rate and/or bufsize and get
1576 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1580 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1585 backend->set_sample_rate (get_rate());
1588 if (change_bufsize) {
1589 backend->set_buffer_size (get_buffer_size());
1593 if (ARDOUR::AudioEngine::instance()->start ()) {
1594 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1604 /* determine if we need to stop the backend before changing parameters */
1606 if (change_driver || change_device || change_channels || change_latency ||
1607 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1609 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1610 restart_required = true;
1612 restart_required = false;
1617 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1618 /* no changes in any parameters that absolutely require a
1619 * restart, so check those that might be changeable without a
1623 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1624 /* can't do this while running ... */
1625 restart_required = true;
1628 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1629 /* can't do this while running ... */
1630 restart_required = true;
1636 if (restart_required) {
1637 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1644 if (change_driver && backend->set_driver (get_driver())) {
1645 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1648 if (change_device && backend->set_device_name (get_device_name())) {
1649 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1652 if (change_rate && backend->set_sample_rate (get_rate())) {
1653 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1656 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1657 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1661 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1662 if (backend->set_input_channels (get_input_channels())) {
1663 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1666 if (backend->set_output_channels (get_output_channels())) {
1667 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1671 if (change_latency) {
1672 if (backend->set_systemic_input_latency (get_input_latency())) {
1673 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1676 if (backend->set_systemic_output_latency (get_output_latency())) {
1677 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1683 backend->set_midi_option (get_midi_option());
1687 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1688 if (_measure_midi) {
1689 if (*p == _measure_midi) {
1690 backend->set_midi_device_enabled ((*p)->name, true);
1692 backend->set_midi_device_enabled ((*p)->name, false);
1696 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1697 if (backend->can_set_systemic_midi_latencies()) {
1698 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1699 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1704 if (start || (was_running && restart_required)) {
1705 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1716 EngineControl::post_push ()
1718 /* get a pointer to the current state object, creating one if
1722 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1725 state = save_state ();
1733 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1734 (*i)->active = false;
1737 /* mark this one active (to be used next time the dialog is
1741 state->active = true;
1743 if (_have_control) { // XXX
1744 manage_control_app_sensitivity ();
1747 /* schedule a redisplay of MIDI ports */
1748 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1753 EngineControl::get_rate () const
1755 float r = atof (sample_rate_combo.get_active_text ());
1756 /* the string may have been translated with an abbreviation for
1757 * thousands, so use a crude heuristic to fix this.
1767 EngineControl::get_buffer_size () const
1769 string txt = buffer_size_combo.get_active_text ();
1772 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1773 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
1774 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
1782 EngineControl::get_midi_option () const
1784 return midi_option_combo.get_active_text();
1788 EngineControl::get_input_channels() const
1790 if (ARDOUR::Profile->get_mixbus()) {
1791 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1792 if (!backend) return 0;
1793 return backend->input_channels();
1795 return (uint32_t) input_channels_adjustment.get_value();
1799 EngineControl::get_output_channels() const
1801 if (ARDOUR::Profile->get_mixbus()) {
1802 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1803 if (!backend) return 0;
1804 return backend->input_channels();
1806 return (uint32_t) output_channels_adjustment.get_value();
1810 EngineControl::get_input_latency() const
1812 return (uint32_t) input_latency_adjustment.get_value();
1816 EngineControl::get_output_latency() const
1818 return (uint32_t) output_latency_adjustment.get_value();
1822 EngineControl::get_backend () const
1824 return backend_combo.get_active_text ();
1828 EngineControl::get_driver () const
1830 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1831 return driver_combo.get_active_text ();
1838 EngineControl::get_device_name () const
1840 return device_combo.get_active_text ();
1844 EngineControl::control_app_button_clicked ()
1846 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1852 backend->launch_control_app ();
1856 EngineControl::manage_control_app_sensitivity ()
1858 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1864 string appname = backend->control_app_name();
1866 if (appname.empty()) {
1867 control_app_button.set_sensitive (false);
1869 control_app_button.set_sensitive (true);
1874 EngineControl::set_desired_sample_rate (uint32_t sr)
1876 _desired_sample_rate = sr;
1881 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1883 if (page_num == 0) {
1884 cancel_button->set_sensitive (true);
1885 ok_button->set_sensitive (true);
1886 apply_button->set_sensitive (true);
1887 _measure_midi.reset();
1889 cancel_button->set_sensitive (false);
1890 ok_button->set_sensitive (false);
1891 apply_button->set_sensitive (false);
1894 if (page_num == midi_tab) {
1896 refresh_midi_display ();
1899 if (page_num == latency_tab) {
1902 if (ARDOUR::AudioEngine::instance()->running()) {
1903 // TODO - mark as 'stopped for latency
1904 ARDOUR_UI::instance()->disconnect_from_engine ();
1908 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1910 /* save any existing latency values */
1912 uint32_t il = (uint32_t) input_latency.get_value ();
1913 uint32_t ol = (uint32_t) input_latency.get_value ();
1915 /* reset to zero so that our new test instance
1916 will be clean of any existing latency measures.
1918 NB. this should really be done by the backend
1919 when stated for latency measurement.
1922 input_latency.set_value (0);
1923 output_latency.set_value (0);
1925 push_state_to_backend (false);
1929 input_latency.set_value (il);
1930 output_latency.set_value (ol);
1933 // This should be done in push_state_to_backend()
1934 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1935 disable_latency_tab ();
1938 enable_latency_tab ();
1942 end_latency_detection ();
1943 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1948 /* latency measurement */
1951 EngineControl::check_audio_latency_measurement ()
1953 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1955 if (mtdm->resolve () < 0) {
1956 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1960 if (mtdm->err () > 0.3) {
1966 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1968 if (sample_rate == 0) {
1969 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1970 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1974 int frames_total = mtdm->del();
1975 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1977 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1978 _("Detected roundtrip latency: "),
1979 frames_total, frames_total * 1000.0f/sample_rate,
1980 _("Systemic latency: "),
1981 extra, extra * 1000.0f/sample_rate);
1985 if (mtdm->err () > 0.2) {
1987 strcat (buf, _("(signal detection error)"));
1993 strcat (buf, _("(inverted - bad wiring)"));
1997 lm_results.set_markup (string_compose (results_markup, buf));
2000 have_lm_results = true;
2001 end_latency_detection ();
2002 lm_use_button.set_sensitive (true);
2010 EngineControl::check_midi_latency_measurement ()
2012 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2014 if (!mididm->have_signal () || mididm->latency () == 0) {
2015 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2020 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2022 if (sample_rate == 0) {
2023 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2024 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2028 ARDOUR::framecnt_t frames_total = mididm->latency();
2029 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2030 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2031 _("Detected roundtrip latency: "),
2032 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2033 _("Systemic latency: "),
2034 extra, extra * 1000.0f / sample_rate);
2038 if (!mididm->ok ()) {
2040 strcat (buf, _("(averaging)"));
2044 if (mididm->deviation () > 50.0) {
2046 strcat (buf, _("(too large jitter)"));
2048 } else if (mididm->deviation () > 10.0) {
2050 strcat (buf, _("(large jitter)"));
2054 have_lm_results = true;
2055 end_latency_detection ();
2056 lm_use_button.set_sensitive (true);
2057 lm_results.set_markup (string_compose (results_markup, buf));
2059 } else if (mididm->processed () > 400) {
2060 have_lm_results = false;
2061 end_latency_detection ();
2062 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2066 lm_results.set_markup (string_compose (results_markup, buf));
2072 EngineControl::start_latency_detection ()
2074 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2075 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2077 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2078 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2079 if (_measure_midi) {
2080 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2082 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2084 lm_measure_label.set_text (_("Cancel"));
2085 have_lm_results = false;
2086 lm_use_button.set_sensitive (false);
2087 lm_input_channel_combo.set_sensitive (false);
2088 lm_output_channel_combo.set_sensitive (false);
2094 EngineControl::end_latency_detection ()
2096 latency_timeout.disconnect ();
2097 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2098 lm_measure_label.set_text (_("Measure"));
2099 if (!have_lm_results) {
2100 lm_use_button.set_sensitive (false);
2102 lm_input_channel_combo.set_sensitive (true);
2103 lm_output_channel_combo.set_sensitive (true);
2108 EngineControl::latency_button_clicked ()
2111 start_latency_detection ();
2113 end_latency_detection ();
2118 EngineControl::use_latency_button_clicked ()
2120 if (_measure_midi) {
2121 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2125 ARDOUR::framecnt_t frames_total = mididm->latency();
2126 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2127 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2128 _measure_midi->input_latency = one_way;
2129 _measure_midi->output_latency = one_way;
2130 notebook.set_current_page (midi_tab);
2132 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2138 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2139 one_way = std::max (0., one_way);
2141 input_latency_adjustment.set_value (one_way);
2142 output_latency_adjustment.set_value (one_way);
2144 /* back to settings page */
2145 notebook.set_current_page (0);
2151 EngineControl::on_delete_event (GdkEventAny* ev)
2153 if (notebook.get_current_page() == 2) {
2154 /* currently on latency tab - be sure to clean up */
2155 end_latency_detection ();
2157 return ArdourDialog::on_delete_event (ev);
2161 EngineControl::engine_running ()
2163 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2166 set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2167 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2169 buffer_size_combo.set_sensitive (true);
2170 sample_rate_combo.set_sensitive (true);
2172 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2173 connect_disconnect_button.show();
2175 started_at_least_once = true;
2176 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2180 EngineControl::engine_stopped ()
2182 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2185 buffer_size_combo.set_sensitive (false);
2186 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2187 connect_disconnect_button.show();
2189 sample_rate_combo.set_sensitive (true);
2190 buffer_size_combo.set_sensitive (true);
2191 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2195 EngineControl::device_list_changed ()
2197 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1); // ??
2199 midi_option_changed();
2203 EngineControl::connect_disconnect_click()
2205 if (ARDOUR::AudioEngine::instance()->running()) {
2206 ARDOUR_UI::instance()->disconnect_from_engine ();
2208 ARDOUR_UI::instance()->reconnect_to_engine ();
2213 EngineControl::calibrate_audio_latency ()
2215 _measure_midi.reset ();
2216 have_lm_results = false;
2217 lm_use_button.set_sensitive (false);
2218 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2219 notebook.set_current_page (latency_tab);
2223 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2226 have_lm_results = false;
2227 lm_use_button.set_sensitive (false);
2228 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2229 notebook.set_current_page (latency_tab);
2233 EngineControl::configure_midi_devices ()
2235 notebook.set_current_page (midi_tab);