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"
47 #include "pbd/convert.h"
48 #include "pbd/error.h"
50 #include "ardour_ui.h"
51 #include "engine_dialog.h"
52 #include "gui_thread.h"
58 using namespace Gtkmm2ext;
62 static const unsigned int midi_tab = 2;
63 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
65 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
67 EngineControl::EngineControl ()
68 : ArdourDialog (_("Audio/MIDI Setup"))
70 , input_latency_adjustment (0, 0, 99999, 1)
71 , input_latency (input_latency_adjustment)
72 , output_latency_adjustment (0, 0, 99999, 1)
73 , output_latency (output_latency_adjustment)
74 , input_channels_adjustment (0, 0, 256, 1)
75 , input_channels (input_channels_adjustment)
76 , output_channels_adjustment (0, 0, 256, 1)
77 , output_channels (output_channels_adjustment)
78 , ports_adjustment (128, 8, 1024, 1, 16)
79 , ports_spinner (ports_adjustment)
80 , control_app_button (_("Device Control Panel"))
81 , midi_devices_button (_("Midi Device Setup"))
82 , lm_measure_label (_("Measure"))
83 , lm_use_button (_("Use results"))
84 , lm_back_button (_("Back to settings ... (ignore results)"))
85 , lm_button_audio (_("Calibrate Audio"))
87 , have_lm_results (false)
89 , midi_back_button (_("Back to settings"))
91 , _desired_sample_rate (0)
92 , started_at_least_once (false)
94 using namespace Notebook_Helpers;
95 vector<string> strings;
97 AttachOptions xopt = AttachOptions (FILL|EXPAND);
100 set_name (X_("AudioMIDISetup"));
102 /* the backend combo is the one thing that is ALWAYS visible */
104 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
106 if (backends.empty()) {
107 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));
109 throw failed_constructor ();
112 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
113 strings.push_back ((*b)->name);
116 set_popdown_strings (backend_combo, strings);
117 backend_combo.set_active_text (strings.front());
118 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
120 /* setup basic packing characteristics for the table used on the main
121 * tab of the notebook
124 basic_packer.set_spacings (6);
125 basic_packer.set_border_width (12);
126 basic_packer.set_homogeneous (false);
130 basic_hbox.pack_start (basic_packer, false, false);
132 /* latency measurement tab */
134 lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
137 lm_table.set_row_spacings (12);
138 lm_table.set_col_spacings (6);
139 lm_table.set_homogeneous (false);
141 lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
144 lm_preamble.set_width_chars (60);
145 lm_preamble.set_line_wrap (true);
146 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
148 lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
151 Gtk::Label* preamble;
152 preamble = manage (new Label);
153 preamble->set_width_chars (60);
154 preamble->set_line_wrap (true);
155 preamble->set_markup (_("Select two channels below and connect them using a cable."));
157 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
160 label = manage (new Label (_("Output channel")));
161 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
163 Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
164 misc_align->add (lm_output_channel_combo);
165 lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
168 label = manage (new Label (_("Input channel")));
169 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
171 misc_align = manage (new Alignment (0.0, 0.5));
172 misc_align->add (lm_input_channel_combo);
173 lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
176 xopt = AttachOptions(0);
178 lm_measure_label.set_padding (10, 10);
179 lm_measure_button.add (lm_measure_label);
180 lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
181 lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
182 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
184 lm_use_button.set_sensitive (false);
186 /* Increase the default spacing around the labels of these three
192 if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
193 l->set_padding (10, 10);
196 if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
197 l->set_padding (10, 10);
200 preamble = manage (new Label);
201 preamble->set_width_chars (60);
202 preamble->set_line_wrap (true);
203 preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
204 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
207 preamble = manage (new Label);
208 preamble->set_width_chars (60);
209 preamble->set_line_wrap (true);
210 preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
211 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
213 ++row; // skip a row in the table
214 ++row; // skip a row in the table
216 lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
218 ++row; // skip a row in the table
219 ++row; // skip a row in the table
221 lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
222 lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
223 lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
225 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
227 lm_vbox.set_border_width (12);
228 lm_vbox.pack_start (lm_table, false, false);
230 midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
234 notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
235 notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
236 notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
237 notebook.set_border_width (12);
239 notebook.set_show_tabs (false);
240 notebook.show_all ();
242 notebook.set_name ("SettingsNotebook");
244 /* packup the notebook */
246 get_vbox()->set_border_width (12);
247 get_vbox()->pack_start (notebook);
249 /* need a special function to print "all available channels" when the
250 * channel counts hit zero.
253 input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
254 output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
256 midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
257 midi_devices_button.set_sensitive (false);
258 midi_devices_button.set_name ("generic button");
259 midi_devices_button.set_can_focus(true);
261 control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
262 manage_control_app_sensitivity ();
264 cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
265 ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
266 apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
268 /* Pick up any existing audio setup configuration, if appropriate */
270 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
272 ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
273 ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
274 ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
278 set_state (*audio_setup);
281 /* ignore: don't save state */
282 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
286 /* Connect to signals */
288 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
289 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
290 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
291 device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
292 midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
294 input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
295 output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
296 input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
297 output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
299 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
303 EngineControl::on_response (int response_id)
305 ArdourDialog::on_response (response_id);
307 switch (response_id) {
309 push_state_to_backend (true);
312 push_state_to_backend (true);
315 case RESPONSE_DELETE_EVENT:
318 ev.type = GDK_BUTTON_PRESS;
320 on_delete_event ((GdkEventAny*) &ev);
329 EngineControl::build_notebook ()
332 AttachOptions xopt = AttachOptions (FILL|EXPAND);
334 /* clear the table */
336 Gtkmm2ext::container_clear (basic_vbox);
337 Gtkmm2ext::container_clear (basic_packer);
339 if (control_app_button.get_parent()) {
340 control_app_button.get_parent()->remove (control_app_button);
343 label = manage (left_aligned_label (_("Audio System:")));
344 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
345 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
347 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
348 lm_button_audio.set_name ("generic button");
349 lm_button_audio.set_can_focus(true);
352 build_full_control_notebook ();
354 build_no_control_notebook ();
357 basic_vbox.pack_start (basic_hbox, false, false);
360 Gtk::HBox* hpacker = manage (new HBox);
361 hpacker->set_border_width (12);
362 hpacker->pack_start (control_app_button, false, false);
364 control_app_button.show();
365 basic_vbox.pack_start (*hpacker);
368 basic_vbox.show_all ();
372 EngineControl::build_full_control_notebook ()
374 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
377 using namespace Notebook_Helpers;
379 vector<string> strings;
380 AttachOptions xopt = AttachOptions (FILL|EXPAND);
381 int row = 1; // row zero == backend combo
383 /* start packing it up */
385 if (backend->requires_driver_selection()) {
386 label = manage (left_aligned_label (_("Driver:")));
387 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
388 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
392 label = manage (left_aligned_label (_("Device:")));
393 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
394 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
397 label = manage (left_aligned_label (_("Sample rate:")));
398 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
399 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
403 label = manage (left_aligned_label (_("Buffer size:")));
404 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
405 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
406 buffer_size_duration_label.set_alignment (0.0); /* left-align */
407 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
410 input_channels.set_name ("InputChannels");
411 input_channels.set_flags (Gtk::CAN_FOCUS);
412 input_channels.set_digits (0);
413 input_channels.set_wrap (false);
414 output_channels.set_editable (true);
416 label = manage (left_aligned_label (_("Input Channels:")));
417 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
418 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
421 output_channels.set_name ("OutputChannels");
422 output_channels.set_flags (Gtk::CAN_FOCUS);
423 output_channels.set_digits (0);
424 output_channels.set_wrap (false);
425 output_channels.set_editable (true);
427 label = manage (left_aligned_label (_("Output Channels:")));
428 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
429 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
432 input_latency.set_name ("InputLatency");
433 input_latency.set_flags (Gtk::CAN_FOCUS);
434 input_latency.set_digits (0);
435 input_latency.set_wrap (false);
436 input_latency.set_editable (true);
438 label = manage (left_aligned_label (_("Hardware input latency:")));
439 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
440 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
441 label = manage (left_aligned_label (_("samples")));
442 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
445 output_latency.set_name ("OutputLatency");
446 output_latency.set_flags (Gtk::CAN_FOCUS);
447 output_latency.set_digits (0);
448 output_latency.set_wrap (false);
449 output_latency.set_editable (true);
451 label = manage (left_aligned_label (_("Hardware output latency:")));
452 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
453 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
454 label = manage (left_aligned_label (_("samples")));
455 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
457 /* button spans 2 rows */
459 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
462 label = manage (left_aligned_label (_("MIDI System")));
463 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
464 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
465 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
470 EngineControl::build_no_control_notebook ()
472 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
475 using namespace Notebook_Helpers;
477 vector<string> strings;
478 AttachOptions xopt = AttachOptions (FILL|EXPAND);
479 int row = 1; // row zero == backend combo
480 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
482 label = manage (new Label);
483 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
484 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
487 if (backend->can_change_sample_rate_when_running()) {
488 label = manage (left_aligned_label (_("Sample rate:")));
489 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
490 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
494 if (backend->can_change_buffer_size_when_running()) {
495 label = manage (left_aligned_label (_("Buffer size:")));
496 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
497 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
498 buffer_size_duration_label.set_alignment (0.0); /* left-align */
499 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
503 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
505 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
509 EngineControl::~EngineControl ()
511 ignore_changes = true;
515 EngineControl::disable_latency_tab ()
517 vector<string> empty;
518 set_popdown_strings (lm_output_channel_combo, empty);
519 set_popdown_strings (lm_input_channel_combo, empty);
520 lm_measure_button.set_sensitive (false);
521 lm_use_button.set_sensitive (false);
525 EngineControl::enable_latency_tab ()
527 vector<string> outputs;
528 vector<string> inputs;
530 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
531 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
532 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
534 if (!ARDOUR::AudioEngine::instance()->running()) {
535 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
536 notebook.set_current_page (0);
540 else if (inputs.empty() || outputs.empty()) {
541 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
542 notebook.set_current_page (0);
547 lm_back_button_signal.disconnect();
549 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
550 lm_preamble.set_markup (_(""));
552 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
553 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
556 set_popdown_strings (lm_output_channel_combo, outputs);
557 lm_output_channel_combo.set_active_text (outputs.front());
558 lm_output_channel_combo.set_sensitive (true);
560 set_popdown_strings (lm_input_channel_combo, inputs);
561 lm_input_channel_combo.set_active_text (inputs.front());
562 lm_input_channel_combo.set_sensitive (true);
564 lm_measure_button.set_sensitive (true);
568 EngineControl::setup_midi_tab_for_backend ()
570 string backend = backend_combo.get_active_text ();
572 Gtkmm2ext::container_clear (midi_vbox);
574 midi_vbox.set_border_width (12);
575 midi_device_table.set_border_width (12);
577 if (backend == "JACK") {
578 setup_midi_tab_for_jack ();
581 midi_vbox.pack_start (midi_device_table, true, true);
582 midi_vbox.pack_start (midi_back_button, false, false);
583 midi_vbox.show_all ();
587 EngineControl::setup_midi_tab_for_jack ()
592 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
594 device->input_latency = a->get_value();
596 device->output_latency = a->get_value();
601 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
602 b->set_active (!b->get_active());
603 device->enabled = b->get_active();
604 refresh_midi_display(device->name);
608 EngineControl::refresh_midi_display (std::string focus)
610 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
614 AttachOptions xopt = AttachOptions (FILL|EXPAND);
617 Gtkmm2ext::container_clear (midi_device_table);
619 midi_device_table.set_spacings (6);
621 l = manage (new Label);
622 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
623 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
624 l->set_alignment (0.5, 0.5);
628 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
629 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
630 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
631 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
633 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
634 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
635 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
636 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
639 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
644 bool enabled = (*p)->enabled;
646 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
647 m->set_name ("midi device");
648 m->set_can_focus (Gtk::CAN_FOCUS);
649 m->add_events (Gdk::BUTTON_RELEASE_MASK);
650 m->set_active (enabled);
651 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
652 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
653 if ((*p)->name == focus) {
657 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
658 s = manage (new Gtk::SpinButton (*a));
659 a->set_value ((*p)->input_latency);
660 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
661 s->set_sensitive (_can_set_midi_latencies && enabled);
662 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
664 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
665 s = manage (new Gtk::SpinButton (*a));
666 a->set_value ((*p)->output_latency);
667 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
668 s->set_sensitive (_can_set_midi_latencies && enabled);
669 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
671 b = manage (new Button (_("Calibrate")));
672 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
673 b->set_sensitive (_can_set_midi_latencies && enabled);
674 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
681 EngineControl::update_sensitivity ()
686 EngineControl::backend_changed ()
688 string backend_name = backend_combo.get_active_text();
689 boost::shared_ptr<ARDOUR::AudioBackend> backend;
691 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
692 /* eh? setting the backend failed... how ? */
696 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
699 setup_midi_tab_for_backend ();
700 _midi_devices.clear();
702 if (backend->requires_driver_selection()) {
703 vector<string> drivers = backend->enumerate_drivers();
704 driver_combo.set_sensitive (true);
706 if (!drivers.empty()) {
708 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
709 set_popdown_strings (driver_combo, drivers);
710 driver_combo.set_active_text (drivers.front());
717 driver_combo.set_sensitive (false);
718 /* this will change the device text which will cause a call to
719 * device changed which will set up parameters
724 vector<string> midi_options = backend->enumerate_midi_options();
726 if (midi_options.size() == 1) {
727 /* only contains the "none" option */
728 midi_option_combo.set_sensitive (false);
731 set_popdown_strings (midi_option_combo, midi_options);
732 midi_option_combo.set_active_text (midi_options.front());
733 midi_option_combo.set_sensitive (true);
735 midi_option_combo.set_sensitive (false);
739 midi_option_changed();
741 started_at_least_once = false;
743 if (!ignore_changes) {
744 maybe_display_saved_state ();
749 EngineControl::print_channel_count (Gtk::SpinButton* sb)
751 uint32_t cnt = (uint32_t) sb->get_value();
753 sb->set_text (_("all available channels"));
756 snprintf (buf, sizeof (buf), "%d", cnt);
763 EngineControl::list_devices ()
765 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
768 /* now fill out devices, mark sample rates, buffer sizes insensitive */
770 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
772 /* NOTE: Ardour currently does not display the "available" field of the
775 * Doing so would require a different GUI widget than the combo
776 * box/popdown that we currently use, since it has no way to list
777 * items that are not selectable. Something more like a popup menu,
778 * which could have unselectable items, would be appropriate.
781 vector<string> available_devices;
783 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
784 available_devices.push_back (i->name);
787 if (!available_devices.empty()) {
789 update_sensitivity ();
792 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
793 set_popdown_strings (device_combo, available_devices);
794 device_combo.set_active_text (available_devices.front());
799 ok_button->set_sensitive (true);
800 apply_button->set_sensitive (true);
803 device_combo.clear();
804 sample_rate_combo.set_sensitive (false);
805 buffer_size_combo.set_sensitive (false);
806 input_latency.set_sensitive (false);
807 output_latency.set_sensitive (false);
808 input_channels.set_sensitive (false);
809 output_channels.set_sensitive (false);
811 ok_button->set_sensitive (false);
812 apply_button->set_sensitive (false);
814 ok_button->set_sensitive (true);
815 apply_button->set_sensitive (true);
821 EngineControl::driver_changed ()
823 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
826 backend->set_driver (driver_combo.get_active_text());
829 if (!ignore_changes) {
830 maybe_display_saved_state ();
835 EngineControl::device_changed ()
838 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
840 string device_name = device_combo.get_active_text ();
844 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
846 /* don't allow programmatic change to combos to cause a
847 recursive call to this method.
857 sr = backend->available_sample_rates (device_name);
860 sr.push_back (8000.0f);
861 sr.push_back (16000.0f);
862 sr.push_back (32000.0f);
863 sr.push_back (44100.0f);
864 sr.push_back (48000.0f);
865 sr.push_back (88200.0f);
866 sr.push_back (96000.0f);
867 sr.push_back (192000.0f);
868 sr.push_back (384000.0f);
871 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
872 s.push_back (rate_as_string (*x));
873 if (*x == _desired_sample_rate) {
879 sample_rate_combo.set_sensitive (true);
880 set_popdown_strings (sample_rate_combo, s);
882 if (desired.empty()) {
883 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
885 sample_rate_combo.set_active_text (desired);
889 sample_rate_combo.set_sensitive (false);
897 bs = backend->available_buffer_sizes (device_name);
898 } else if (backend->can_change_buffer_size_when_running()) {
912 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
913 s.push_back (bufsize_as_string (*x));
917 buffer_size_combo.set_sensitive (true);
918 set_popdown_strings (buffer_size_combo, s);
920 buffer_size_combo.set_active_text (bufsize_as_string (backend->default_buffer_size()));
921 show_buffer_duration ();
923 buffer_size_combo.set_sensitive (false);
926 /* XXX theoretically need to set min + max channel counts here
929 manage_control_app_sensitivity ();
932 /* pick up any saved state for this device */
934 if (!ignore_changes) {
935 maybe_display_saved_state ();
940 EngineControl::bufsize_as_string (uint32_t sz)
942 /* Translators: "samples" is always plural here, so no
943 need for plural+singular forms.
946 snprintf (buf, sizeof (buf), _("%u samples"), sz);
951 EngineControl::sample_rate_changed ()
953 /* reset the strings for buffer size to show the correct msec value
954 (reflecting the new sample rate).
957 show_buffer_duration ();
958 if (!ignore_changes) {
965 EngineControl::buffer_size_changed ()
967 show_buffer_duration ();
968 if (!ignore_changes) {
974 EngineControl::show_buffer_duration ()
977 /* buffer sizes - convert from just samples to samples + msecs for
978 * the displayed string
981 string bs_text = buffer_size_combo.get_active_text ();
982 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
983 uint32_t rate = get_rate();
985 /* Translators: "msecs" is ALWAYS plural here, so we do not
986 need singular form as well.
988 /* Developers: note the hard-coding of a double buffered model
989 in the (2 * samples) computation of latency. we always start
990 the audiobackend in this configuration.
993 snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
994 buffer_size_duration_label.set_text (buf);
998 EngineControl::midi_option_changed ()
1000 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1003 backend->set_midi_option (get_midi_option());
1005 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1007 //_midi_devices.clear(); // TODO merge with state-saved settings..
1008 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1009 std::vector<MidiDeviceSettings> new_devices;
1011 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1012 MidiDeviceSettings mds = find_midi_device (i->name);
1013 if (i->available && !mds) {
1014 uint32_t input_latency = 0;
1015 uint32_t output_latency = 0;
1016 if (_can_set_midi_latencies) {
1017 input_latency = backend->systemic_midi_input_latency (i->name);
1018 output_latency = backend->systemic_midi_output_latency (i->name);
1020 bool enabled = backend->midi_device_enabled (i->name);
1021 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1022 new_devices.push_back (ptr);
1023 } else if (i->available) {
1024 new_devices.push_back (mds);
1027 _midi_devices = new_devices;
1029 if (_midi_devices.empty()) {
1030 midi_devices_button.set_sensitive (false);
1032 midi_devices_button.set_sensitive (true);
1035 if (!ignore_changes) {
1041 EngineControl::parameter_changed ()
1043 if (!ignore_changes) {
1048 EngineControl::State
1049 EngineControl::get_matching_state (
1050 const string& backend,
1051 const string& driver,
1052 const string& device)
1054 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1055 if ((*i)->backend == backend &&
1056 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1064 EngineControl::State
1065 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1067 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1070 return get_matching_state (backend_combo.get_active_text(),
1071 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1072 device_combo.get_active_text());
1076 return get_matching_state (backend_combo.get_active_text(),
1078 device_combo.get_active_text());
1081 EngineControl::State
1082 EngineControl::save_state ()
1086 if (!_have_control) {
1087 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1091 state.reset(new StateStruct);
1092 state->backend = get_backend ();
1094 state.reset(new StateStruct);
1095 store_state (state);
1098 for (StateList::iterator i = states.begin(); i != states.end();) {
1099 if ((*i)->backend == state->backend &&
1100 (*i)->driver == state->driver &&
1101 (*i)->device == state->device) {
1102 i = states.erase(i);
1108 states.push_back (state);
1114 EngineControl::store_state (State state)
1116 state->backend = get_backend ();
1117 state->driver = get_driver ();
1118 state->device = get_device_name ();
1119 state->sample_rate = get_rate ();
1120 state->buffer_size = get_buffer_size ();
1121 state->input_latency = get_input_latency ();
1122 state->output_latency = get_output_latency ();
1123 state->input_channels = get_input_channels ();
1124 state->output_channels = get_output_channels ();
1125 state->midi_option = get_midi_option ();
1126 state->midi_devices = _midi_devices;
1130 EngineControl::maybe_display_saved_state ()
1132 if (!_have_control) {
1136 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1139 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1141 if (!_desired_sample_rate) {
1142 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1144 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
1145 /* call this explicitly because we're ignoring changes to
1146 the controls at this point.
1148 show_buffer_duration ();
1149 input_latency.set_value (state->input_latency);
1150 output_latency.set_value (state->output_latency);
1152 if (!state->midi_option.empty()) {
1153 midi_option_combo.set_active_text (state->midi_option);
1154 _midi_devices = state->midi_devices;
1160 EngineControl::get_state ()
1162 XMLNode* root = new XMLNode ("AudioMIDISetup");
1165 if (!states.empty()) {
1166 XMLNode* state_nodes = new XMLNode ("EngineStates");
1168 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1170 XMLNode* node = new XMLNode ("State");
1172 node->add_property ("backend", (*i)->backend);
1173 node->add_property ("driver", (*i)->driver);
1174 node->add_property ("device", (*i)->device);
1175 node->add_property ("sample-rate", (*i)->sample_rate);
1176 node->add_property ("buffer-size", (*i)->buffer_size);
1177 node->add_property ("input-latency", (*i)->input_latency);
1178 node->add_property ("output-latency", (*i)->output_latency);
1179 node->add_property ("input-channels", (*i)->input_channels);
1180 node->add_property ("output-channels", (*i)->output_channels);
1181 node->add_property ("active", (*i)->active ? "yes" : "no");
1182 node->add_property ("midi-option", (*i)->midi_option);
1184 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1185 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1186 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1187 midi_device_stuff->add_property (X_("name"), (*p)->name);
1188 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1189 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1190 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1191 midi_devices->add_child_nocopy (*midi_device_stuff);
1193 node->add_child_nocopy (*midi_devices);
1195 state_nodes->add_child_nocopy (*node);
1198 root->add_child_nocopy (*state_nodes);
1205 EngineControl::set_state (const XMLNode& root)
1207 XMLNodeList clist, cclist;
1208 XMLNodeConstIterator citer, cciter;
1210 XMLNode* grandchild;
1211 XMLProperty* prop = NULL;
1213 if (root.name() != "AudioMIDISetup") {
1217 clist = root.children();
1221 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1225 if (child->name() != "EngineStates") {
1229 cclist = child->children();
1231 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1232 State state (new StateStruct);
1234 grandchild = *cciter;
1236 if (grandchild->name() != "State") {
1240 if ((prop = grandchild->property ("backend")) == 0) {
1243 state->backend = prop->value ();
1245 if ((prop = grandchild->property ("driver")) == 0) {
1248 state->driver = prop->value ();
1250 if ((prop = grandchild->property ("device")) == 0) {
1253 state->device = prop->value ();
1255 if ((prop = grandchild->property ("sample-rate")) == 0) {
1258 state->sample_rate = atof (prop->value ());
1260 if ((prop = grandchild->property ("buffer-size")) == 0) {
1263 state->buffer_size = atoi (prop->value ());
1265 if ((prop = grandchild->property ("input-latency")) == 0) {
1268 state->input_latency = atoi (prop->value ());
1270 if ((prop = grandchild->property ("output-latency")) == 0) {
1273 state->output_latency = atoi (prop->value ());
1275 if ((prop = grandchild->property ("input-channels")) == 0) {
1278 state->input_channels = atoi (prop->value ());
1280 if ((prop = grandchild->property ("output-channels")) == 0) {
1283 state->output_channels = atoi (prop->value ());
1285 if ((prop = grandchild->property ("active")) == 0) {
1288 state->active = string_is_affirmative (prop->value ());
1290 if ((prop = grandchild->property ("midi-option")) == 0) {
1293 state->midi_option = prop->value ();
1295 state->midi_devices.clear();
1297 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1298 const XMLNodeList mnc = midinode->children();
1299 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1300 if ((*n)->property (X_("name")) == 0
1301 || (*n)->property (X_("enabled")) == 0
1302 || (*n)->property (X_("input-latency")) == 0
1303 || (*n)->property (X_("output-latency")) == 0
1308 MidiDeviceSettings ptr (new MidiDeviceSetting(
1309 (*n)->property (X_("name"))->value (),
1310 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1311 atoi ((*n)->property (X_("input-latency"))->value ()),
1312 atoi ((*n)->property (X_("output-latency"))->value ())
1314 state->midi_devices.push_back (ptr);
1319 /* remove accumulated duplicates (due to bug in ealier version)
1320 * this can be removed again before release
1322 for (StateList::iterator i = states.begin(); i != states.end();) {
1323 if ((*i)->backend == state->backend &&
1324 (*i)->driver == state->driver &&
1325 (*i)->device == state->device) {
1326 i = states.erase(i);
1333 states.push_back (state);
1337 /* now see if there was an active state and switch the setup to it */
1339 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1343 backend_combo.set_active_text ((*i)->backend);
1344 driver_combo.set_active_text ((*i)->driver);
1345 device_combo.set_active_text ((*i)->device);
1346 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1347 buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
1348 input_latency.set_value ((*i)->input_latency);
1349 output_latency.set_value ((*i)->output_latency);
1350 midi_option_combo.set_active_text ((*i)->midi_option);
1358 EngineControl::push_state_to_backend (bool start)
1360 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1366 /* figure out what is going to change */
1368 bool restart_required = false;
1369 bool was_running = ARDOUR::AudioEngine::instance()->running();
1370 bool change_driver = false;
1371 bool change_device = false;
1372 bool change_rate = false;
1373 bool change_bufsize = false;
1374 bool change_latency = false;
1375 bool change_channels = false;
1376 bool change_midi = false;
1378 uint32_t ochan = get_output_channels ();
1379 uint32_t ichan = get_input_channels ();
1381 if (_have_control) {
1383 if (started_at_least_once) {
1385 /* we can control the backend */
1387 if (backend->requires_driver_selection()) {
1388 if (get_driver() != backend->driver_name()) {
1389 change_driver = true;
1393 if (get_device_name() != backend->device_name()) {
1394 change_device = true;
1397 if (get_rate() != backend->sample_rate()) {
1401 if (get_buffer_size() != backend->buffer_size()) {
1402 change_bufsize = true;
1405 if (get_midi_option() != backend->midi_option()) {
1409 /* zero-requested channels means "all available" */
1412 ichan = backend->input_channels();
1416 ochan = backend->output_channels();
1419 if (ichan != backend->input_channels()) {
1420 change_channels = true;
1423 if (ochan != backend->output_channels()) {
1424 change_channels = true;
1427 if (get_input_latency() != backend->systemic_input_latency() ||
1428 get_output_latency() != backend->systemic_output_latency()) {
1429 change_latency = true;
1432 /* backend never started, so we have to force a group
1435 change_device = true;
1436 if (backend->requires_driver_selection()) {
1437 change_driver = true;
1440 change_bufsize = true;
1441 change_channels = true;
1442 change_latency = true;
1448 /* we have no control over the backend, meaning that we can
1449 * only possibly change sample rate and buffer size.
1453 if (get_rate() != backend->sample_rate()) {
1454 change_bufsize = true;
1457 if (get_buffer_size() != backend->buffer_size()) {
1458 change_bufsize = true;
1462 if (!_have_control) {
1464 /* We do not have control over the backend, so the best we can
1465 * do is try to change the sample rate and/or bufsize and get
1469 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1473 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1478 backend->set_sample_rate (get_rate());
1481 if (change_bufsize) {
1482 backend->set_buffer_size (get_buffer_size());
1486 if (ARDOUR::AudioEngine::instance()->start ()) {
1487 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1497 /* determine if we need to stop the backend before changing parameters */
1499 if (change_driver || change_device || change_channels || change_latency ||
1500 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1502 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1503 restart_required = true;
1505 restart_required = false;
1510 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1511 /* no changes in any parameters that absolutely require a
1512 * restart, so check those that might be changeable without a
1516 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1517 /* can't do this while running ... */
1518 restart_required = true;
1521 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1522 /* can't do this while running ... */
1523 restart_required = true;
1529 if (restart_required) {
1530 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1537 if (change_driver && backend->set_driver (get_driver())) {
1538 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1541 if (change_device && backend->set_device_name (get_device_name())) {
1542 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1545 if (change_rate && backend->set_sample_rate (get_rate())) {
1546 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1549 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1550 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1554 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1555 if (backend->set_input_channels (get_input_channels())) {
1556 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1559 if (backend->set_output_channels (get_output_channels())) {
1560 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1564 if (change_latency) {
1565 if (backend->set_systemic_input_latency (get_input_latency())) {
1566 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1569 if (backend->set_systemic_output_latency (get_output_latency())) {
1570 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1576 backend->set_midi_option (get_midi_option());
1580 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1581 if (_measure_midi) {
1582 if (*p == _measure_midi) {
1583 backend->set_midi_device_enabled ((*p)->name, true);
1585 backend->set_midi_device_enabled ((*p)->name, false);
1589 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1590 if (backend->can_set_systemic_midi_latencies()) {
1591 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1592 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1597 if (start || (was_running && restart_required)) {
1598 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1609 EngineControl::post_push ()
1611 /* get a pointer to the current state object, creating one if
1615 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1618 state = save_state ();
1624 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1625 (*i)->active = false;
1628 /* mark this one active (to be used next time the dialog is
1632 state->active = true;
1634 if (_have_control) { // XXX
1635 manage_control_app_sensitivity ();
1638 /* schedule a redisplay of MIDI ports */
1639 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1644 EngineControl::get_rate () const
1646 float r = atof (sample_rate_combo.get_active_text ());
1647 /* the string may have been translated with an abbreviation for
1648 * thousands, so use a crude heuristic to fix this.
1658 EngineControl::get_buffer_size () const
1660 string txt = buffer_size_combo.get_active_text ();
1663 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1671 EngineControl::get_midi_option () const
1673 return midi_option_combo.get_active_text();
1677 EngineControl::get_input_channels() const
1679 return (uint32_t) input_channels_adjustment.get_value();
1683 EngineControl::get_output_channels() const
1685 return (uint32_t) output_channels_adjustment.get_value();
1689 EngineControl::get_input_latency() const
1691 return (uint32_t) input_latency_adjustment.get_value();
1695 EngineControl::get_output_latency() const
1697 return (uint32_t) output_latency_adjustment.get_value();
1701 EngineControl::get_backend () const
1703 return backend_combo.get_active_text ();
1707 EngineControl::get_driver () const
1709 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1710 return driver_combo.get_active_text ();
1717 EngineControl::get_device_name () const
1719 return device_combo.get_active_text ();
1723 EngineControl::control_app_button_clicked ()
1725 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1731 backend->launch_control_app ();
1735 EngineControl::manage_control_app_sensitivity ()
1737 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1743 string appname = backend->control_app_name();
1745 if (appname.empty()) {
1746 control_app_button.set_sensitive (false);
1748 control_app_button.set_sensitive (true);
1753 EngineControl::set_desired_sample_rate (uint32_t sr)
1755 _desired_sample_rate = sr;
1760 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1762 if (page_num == 0) {
1763 cancel_button->set_sensitive (true);
1764 ok_button->set_sensitive (true);
1765 apply_button->set_sensitive (true);
1766 _measure_midi.reset();
1768 cancel_button->set_sensitive (false);
1769 ok_button->set_sensitive (false);
1770 apply_button->set_sensitive (false);
1773 if (page_num == midi_tab) {
1775 refresh_midi_display ();
1778 if (page_num == latency_tab) {
1781 if (ARDOUR::AudioEngine::instance()->running()) {
1782 // TODO - mark as 'stopped for latency
1783 ARDOUR_UI::instance()->disconnect_from_engine ();
1787 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1789 /* save any existing latency values */
1791 uint32_t il = (uint32_t) input_latency.get_value ();
1792 uint32_t ol = (uint32_t) input_latency.get_value ();
1794 /* reset to zero so that our new test instance
1795 will be clean of any existing latency measures.
1797 NB. this should really be done by the backend
1798 when stated for latency measurement.
1801 input_latency.set_value (0);
1802 output_latency.set_value (0);
1804 push_state_to_backend (false);
1808 input_latency.set_value (il);
1809 output_latency.set_value (ol);
1812 // This should be done in push_state_to_backend()
1813 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1814 disable_latency_tab ();
1817 enable_latency_tab ();
1821 end_latency_detection ();
1822 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1827 /* latency measurement */
1830 EngineControl::check_audio_latency_measurement ()
1832 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1834 if (mtdm->resolve () < 0) {
1835 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1839 if (mtdm->err () > 0.3) {
1845 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1847 if (sample_rate == 0) {
1848 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1849 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1853 int frames_total = mtdm->del();
1854 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1856 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1857 _("Detected roundtrip latency: "),
1858 frames_total, frames_total * 1000.0f/sample_rate,
1859 _("Systemic latency: "),
1860 extra, extra * 1000.0f/sample_rate);
1864 if (mtdm->err () > 0.2) {
1866 strcat (buf, _("(signal detection error)"));
1872 strcat (buf, _("(inverted - bad wiring)"));
1877 have_lm_results = true;
1878 end_latency_detection ();
1879 lm_use_button.set_sensitive (true);
1883 lm_results.set_markup (string_compose (results_markup, buf));
1889 EngineControl::check_midi_latency_measurement ()
1891 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
1893 if (!mididm->have_signal () || mididm->latency () == 0) {
1894 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1899 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1901 if (sample_rate == 0) {
1902 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1903 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1907 ARDOUR::framecnt_t frames_total = mididm->latency();
1908 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1909 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
1910 _("Detected roundtrip latency: "),
1911 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
1912 _("Systemic latency: "),
1913 extra, extra * 1000.0f / sample_rate);
1917 if (!mididm->ok ()) {
1919 strcat (buf, _("(averaging)"));
1923 if (mididm->deviation () > 50.0) {
1925 strcat (buf, _("(too large jitter)"));
1927 } else if (mididm->deviation () > 10.0) {
1929 strcat (buf, _("(large jitter)"));
1933 have_lm_results = true;
1934 end_latency_detection ();
1935 lm_use_button.set_sensitive (true);
1937 } else if (mididm->processed () > 400) {
1938 have_lm_results = false;
1939 end_latency_detection ();
1940 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
1944 lm_results.set_markup (string_compose (results_markup, buf));
1950 EngineControl::start_latency_detection ()
1952 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1953 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1955 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
1956 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
1957 if (_measure_midi) {
1958 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
1960 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
1962 lm_measure_label.set_text (_("Cancel"));
1963 have_lm_results = false;
1964 lm_use_button.set_sensitive (false);
1965 lm_input_channel_combo.set_sensitive (false);
1966 lm_output_channel_combo.set_sensitive (false);
1972 EngineControl::end_latency_detection ()
1974 latency_timeout.disconnect ();
1975 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1976 lm_measure_label.set_text (_("Measure"));
1977 if (!have_lm_results) {
1978 lm_use_button.set_sensitive (false);
1980 lm_input_channel_combo.set_sensitive (true);
1981 lm_output_channel_combo.set_sensitive (true);
1986 EngineControl::latency_button_clicked ()
1989 start_latency_detection ();
1991 end_latency_detection ();
1996 EngineControl::use_latency_button_clicked ()
1998 if (_measure_midi) {
1999 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2003 ARDOUR::framecnt_t frames_total = mididm->latency();
2004 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2005 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2006 _measure_midi->input_latency = one_way;
2007 _measure_midi->output_latency = one_way;
2008 notebook.set_current_page (midi_tab);
2010 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2016 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2017 one_way = std::max (0., one_way);
2019 input_latency_adjustment.set_value (one_way);
2020 output_latency_adjustment.set_value (one_way);
2022 /* back to settings page */
2023 notebook.set_current_page (0);
2029 EngineControl::on_delete_event (GdkEventAny* ev)
2031 if (notebook.get_current_page() == 2) {
2032 /* currently on latency tab - be sure to clean up */
2033 end_latency_detection ();
2035 return ArdourDialog::on_delete_event (ev);
2039 EngineControl::engine_running ()
2041 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2044 buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
2045 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2047 buffer_size_combo.set_sensitive (true);
2048 sample_rate_combo.set_sensitive (true);
2050 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2052 started_at_least_once = true;
2056 EngineControl::engine_stopped ()
2058 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2061 buffer_size_combo.set_sensitive (false);
2062 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2064 sample_rate_combo.set_sensitive (true);
2065 buffer_size_combo.set_sensitive (true);
2069 EngineControl::connect_disconnect_click()
2071 if (ARDOUR::AudioEngine::instance()->running()) {
2072 ARDOUR_UI::instance()->disconnect_from_engine ();
2074 ARDOUR_UI::instance()->reconnect_to_engine ();
2079 EngineControl::calibrate_audio_latency ()
2081 _measure_midi.reset ();
2082 have_lm_results = false;
2083 lm_use_button.set_sensitive (false);
2084 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2085 notebook.set_current_page (latency_tab);
2089 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2092 have_lm_results = false;
2093 lm_use_button.set_sensitive (false);
2094 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2095 notebook.set_current_page (latency_tab);
2099 EngineControl::configure_midi_devices ()
2101 notebook.set_current_page (midi_tab);