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)
97 using namespace Notebook_Helpers;
98 vector<string> backend_names;
100 AttachOptions xopt = AttachOptions (FILL|EXPAND);
103 set_name (X_("AudioMIDISetup"));
105 /* the backend combo is the one thing that is ALWAYS visible */
107 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
109 if (backends.empty()) {
110 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));
112 throw failed_constructor ();
115 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
116 backend_names.push_back ((*b)->name);
119 set_popdown_strings (backend_combo, backend_names);
120 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
122 /* setup basic packing characteristics for the table used on the main
123 * tab of the notebook
126 basic_packer.set_spacings (6);
127 basic_packer.set_border_width (12);
128 basic_packer.set_homogeneous (false);
132 basic_hbox.pack_start (basic_packer, false, false);
134 /* latency measurement tab */
136 lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
139 lm_table.set_row_spacings (12);
140 lm_table.set_col_spacings (6);
141 lm_table.set_homogeneous (false);
143 lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
146 lm_preamble.set_width_chars (60);
147 lm_preamble.set_line_wrap (true);
148 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
150 lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
153 Gtk::Label* preamble;
154 preamble = manage (new Label);
155 preamble->set_width_chars (60);
156 preamble->set_line_wrap (true);
157 preamble->set_markup (_("Select two channels below and connect them using a cable."));
159 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
162 label = manage (new Label (_("Output channel")));
163 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
165 Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
166 misc_align->add (lm_output_channel_combo);
167 lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
170 label = manage (new Label (_("Input channel")));
171 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
173 misc_align = manage (new Alignment (0.0, 0.5));
174 misc_align->add (lm_input_channel_combo);
175 lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
178 xopt = AttachOptions(0);
180 lm_measure_label.set_padding (10, 10);
181 lm_measure_button.add (lm_measure_label);
182 lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
183 lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
184 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
186 lm_use_button.set_sensitive (false);
188 /* Increase the default spacing around the labels of these three
194 if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
195 l->set_padding (10, 10);
198 if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
199 l->set_padding (10, 10);
202 preamble = manage (new Label);
203 preamble->set_width_chars (60);
204 preamble->set_line_wrap (true);
205 preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
206 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
209 preamble = manage (new Label);
210 preamble->set_width_chars (60);
211 preamble->set_line_wrap (true);
212 preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
213 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
215 ++row; // skip a row in the table
216 ++row; // skip a row in the table
218 lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
220 ++row; // skip a row in the table
221 ++row; // skip a row in the table
223 lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
224 lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
225 lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
227 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
229 lm_vbox.set_border_width (12);
230 lm_vbox.pack_start (lm_table, false, false);
232 midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
236 notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
237 notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
238 notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
239 notebook.set_border_width (12);
241 notebook.set_show_tabs (false);
242 notebook.show_all ();
244 notebook.set_name ("SettingsNotebook");
246 /* packup the notebook */
248 get_vbox()->set_border_width (12);
249 get_vbox()->pack_start (notebook);
251 get_action_area()->pack_start (engine_status);
252 engine_status.show();
254 /* need a special function to print "all available channels" when the
255 * channel counts hit zero.
258 input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
259 output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
261 midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
262 midi_devices_button.set_sensitive (false);
263 midi_devices_button.set_name ("generic button");
264 midi_devices_button.set_can_focus(true);
266 control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
267 manage_control_app_sensitivity ();
269 cancel_button = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL);
270 apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
271 ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
273 /* Pick up any existing audio setup configuration, if appropriate */
275 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
277 ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
278 ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
279 ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
280 ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
283 set_state (*audio_setup);
286 if (backend_combo.get_active_text().empty()) {
287 backend_combo.set_active_text (backend_names.front());
291 /* ignore: don't save state */
292 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
296 /* in case the setting the backend failed, e.g. stale config, from set_state(), try again */
297 if (0 == ARDOUR::AudioEngine::instance()->current_backend()) {
298 backend_combo.set_active_text (backend_names.back());
299 /* ignore: don't save state */
300 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
305 /* Connect to signals */
307 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
308 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
309 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
310 device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
311 midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
313 input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
314 output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
315 input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
316 output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
318 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
320 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
321 connect_disconnect_button.set_no_show_all();
326 EngineControl::on_show ()
328 ArdourDialog::on_show ();
330 ok_button->grab_focus();
334 EngineControl::on_response (int response_id)
336 ArdourDialog::on_response (response_id);
338 switch (response_id) {
340 push_state_to_backend (true);
343 push_state_to_backend (true);
346 case RESPONSE_DELETE_EVENT:
349 ev.type = GDK_BUTTON_PRESS;
351 on_delete_event ((GdkEventAny*) &ev);
360 EngineControl::build_notebook ()
363 AttachOptions xopt = AttachOptions (FILL|EXPAND);
365 /* clear the table */
367 Gtkmm2ext::container_clear (basic_vbox);
368 Gtkmm2ext::container_clear (basic_packer);
370 if (control_app_button.get_parent()) {
371 control_app_button.get_parent()->remove (control_app_button);
374 label = manage (left_aligned_label (_("Audio System:")));
375 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
376 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
378 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
379 lm_button_audio.set_name ("generic button");
380 lm_button_audio.set_can_focus(true);
383 build_full_control_notebook ();
385 build_no_control_notebook ();
388 basic_vbox.pack_start (basic_hbox, false, false);
391 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
392 basic_vbox.show_all ();
397 EngineControl::build_full_control_notebook ()
399 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
402 using namespace Notebook_Helpers;
404 vector<string> strings;
405 AttachOptions xopt = AttachOptions (FILL|EXPAND);
406 int row = 1; // row zero == backend combo
408 /* start packing it up */
410 if (backend->requires_driver_selection()) {
411 label = manage (left_aligned_label (_("Driver:")));
412 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
413 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
417 label = manage (left_aligned_label (_("Device:")));
418 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
419 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
422 label = manage (left_aligned_label (_("Sample rate:")));
423 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
424 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
428 label = manage (left_aligned_label (_("Buffer size:")));
429 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
430 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
431 buffer_size_duration_label.set_alignment (0.0); /* left-align */
432 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
434 /* button spans 2 rows */
436 basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
439 input_channels.set_name ("InputChannels");
440 input_channels.set_flags (Gtk::CAN_FOCUS);
441 input_channels.set_digits (0);
442 input_channels.set_wrap (false);
443 output_channels.set_editable (true);
445 if (!ARDOUR::Profile->get_mixbus()) {
446 label = manage (left_aligned_label (_("Input Channels:")));
447 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
448 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
452 output_channels.set_name ("OutputChannels");
453 output_channels.set_flags (Gtk::CAN_FOCUS);
454 output_channels.set_digits (0);
455 output_channels.set_wrap (false);
456 output_channels.set_editable (true);
458 if (!ARDOUR::Profile->get_mixbus()) {
459 label = manage (left_aligned_label (_("Output Channels:")));
460 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
461 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
465 input_latency.set_name ("InputLatency");
466 input_latency.set_flags (Gtk::CAN_FOCUS);
467 input_latency.set_digits (0);
468 input_latency.set_wrap (false);
469 input_latency.set_editable (true);
471 label = manage (left_aligned_label (_("Hardware input latency:")));
472 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
473 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
474 label = manage (left_aligned_label (_("samples")));
475 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
478 output_latency.set_name ("OutputLatency");
479 output_latency.set_flags (Gtk::CAN_FOCUS);
480 output_latency.set_digits (0);
481 output_latency.set_wrap (false);
482 output_latency.set_editable (true);
484 label = manage (left_aligned_label (_("Hardware output latency:")));
485 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
486 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
487 label = manage (left_aligned_label (_("samples")));
488 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
490 /* button spans 2 rows */
492 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
495 label = manage (left_aligned_label (_("MIDI System")));
496 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
497 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
498 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
503 EngineControl::build_no_control_notebook ()
505 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
508 using namespace Notebook_Helpers;
510 vector<string> strings;
511 AttachOptions xopt = AttachOptions (FILL|EXPAND);
512 int row = 1; // row zero == backend combo
513 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
515 label = manage (new Label);
516 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
517 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
520 if (backend->can_change_sample_rate_when_running()) {
521 label = manage (left_aligned_label (_("Sample rate:")));
522 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
523 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
527 if (backend->can_change_buffer_size_when_running()) {
528 label = manage (left_aligned_label (_("Buffer size:")));
529 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
530 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
531 buffer_size_duration_label.set_alignment (0.0); /* left-align */
532 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
536 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
540 EngineControl::~EngineControl ()
542 ignore_changes = true;
546 EngineControl::disable_latency_tab ()
548 vector<string> empty;
549 set_popdown_strings (lm_output_channel_combo, empty);
550 set_popdown_strings (lm_input_channel_combo, empty);
551 lm_measure_button.set_sensitive (false);
552 lm_use_button.set_sensitive (false);
556 EngineControl::enable_latency_tab ()
558 vector<string> outputs;
559 vector<string> inputs;
561 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
562 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
563 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
565 if (!ARDOUR::AudioEngine::instance()->running()) {
566 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
567 notebook.set_current_page (0);
571 else if (inputs.empty() || outputs.empty()) {
572 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
573 notebook.set_current_page (0);
578 lm_back_button_signal.disconnect();
580 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
583 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
587 set_popdown_strings (lm_output_channel_combo, outputs);
588 lm_output_channel_combo.set_active_text (outputs.front());
589 lm_output_channel_combo.set_sensitive (true);
591 set_popdown_strings (lm_input_channel_combo, inputs);
592 lm_input_channel_combo.set_active_text (inputs.front());
593 lm_input_channel_combo.set_sensitive (true);
595 lm_measure_button.set_sensitive (true);
599 EngineControl::setup_midi_tab_for_backend ()
601 string backend = backend_combo.get_active_text ();
603 Gtkmm2ext::container_clear (midi_vbox);
605 midi_vbox.set_border_width (12);
606 midi_device_table.set_border_width (12);
608 if (backend == "JACK") {
609 setup_midi_tab_for_jack ();
612 midi_vbox.pack_start (midi_device_table, true, true);
613 midi_vbox.pack_start (midi_back_button, false, false);
614 midi_vbox.show_all ();
618 EngineControl::setup_midi_tab_for_jack ()
623 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
625 device->input_latency = a->get_value();
627 device->output_latency = a->get_value();
632 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
633 b->set_active (!b->get_active());
634 device->enabled = b->get_active();
635 refresh_midi_display(device->name);
639 EngineControl::refresh_midi_display (std::string focus)
641 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
645 AttachOptions xopt = AttachOptions (FILL|EXPAND);
648 Gtkmm2ext::container_clear (midi_device_table);
650 midi_device_table.set_spacings (6);
652 l = manage (new Label);
653 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
654 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
655 l->set_alignment (0.5, 0.5);
659 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
660 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
661 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
662 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
664 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
665 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
666 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
667 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
670 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
675 bool enabled = (*p)->enabled;
677 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
678 m->set_name ("midi device");
679 m->set_can_focus (Gtk::CAN_FOCUS);
680 m->add_events (Gdk::BUTTON_RELEASE_MASK);
681 m->set_active (enabled);
682 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
683 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
684 if ((*p)->name == focus) {
688 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
689 s = manage (new Gtk::SpinButton (*a));
690 a->set_value ((*p)->input_latency);
691 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
692 s->set_sensitive (_can_set_midi_latencies && enabled);
693 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
695 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
696 s = manage (new Gtk::SpinButton (*a));
697 a->set_value ((*p)->output_latency);
698 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
699 s->set_sensitive (_can_set_midi_latencies && enabled);
700 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
702 b = manage (new Button (_("Calibrate")));
703 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
704 b->set_sensitive (_can_set_midi_latencies && enabled);
705 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
712 EngineControl::update_sensitivity ()
717 EngineControl::backend_changed ()
719 string backend_name = backend_combo.get_active_text();
720 boost::shared_ptr<ARDOUR::AudioBackend> backend;
722 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
723 /* eh? setting the backend failed... how ? */
724 /* A: stale config contains a backend that does not exist in current build */
728 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
731 setup_midi_tab_for_backend ();
732 _midi_devices.clear();
734 if (backend->requires_driver_selection()) {
735 vector<string> drivers = backend->enumerate_drivers();
736 driver_combo.set_sensitive (true);
738 if (!drivers.empty()) {
740 string current_driver;
741 current_driver = backend->driver_name ();
743 // driver might not have been set yet
744 if (current_driver == "") {
745 current_driver = driver_combo.get_active_text ();
746 if (current_driver == "")
747 // driver has never been set, make sure it's not blank
748 current_driver = drivers.front ();
751 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
752 set_popdown_strings (driver_combo, drivers);
753 driver_combo.set_active_text (current_driver);
760 driver_combo.set_sensitive (false);
761 /* this will change the device text which will cause a call to
762 * device changed which will set up parameters
767 vector<string> midi_options = backend->enumerate_midi_options();
769 if (midi_options.size() == 1) {
770 /* only contains the "none" option */
771 midi_option_combo.set_sensitive (false);
774 set_popdown_strings (midi_option_combo, midi_options);
775 midi_option_combo.set_active_text (midi_options.front());
776 midi_option_combo.set_sensitive (true);
778 midi_option_combo.set_sensitive (false);
782 connect_disconnect_button.hide();
784 midi_option_changed();
786 started_at_least_once = false;
788 if (!ignore_changes) {
789 maybe_display_saved_state ();
794 EngineControl::print_channel_count (Gtk::SpinButton* sb)
796 if (ARDOUR::Profile->get_mixbus()) {
797 cout << "Mixbus crash trap. sb->get_value(): " << sb->get_value();
801 uint32_t cnt = (uint32_t) sb->get_value();
803 sb->set_text (_("all available channels"));
806 snprintf (buf, sizeof (buf), "%d", cnt);
813 EngineControl::list_devices ()
815 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
818 /* now fill out devices, mark sample rates, buffer sizes insensitive */
820 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
822 /* NOTE: Ardour currently does not display the "available" field of the
825 * Doing so would require a different GUI widget than the combo
826 * box/popdown that we currently use, since it has no way to list
827 * items that are not selectable. Something more like a popup menu,
828 * which could have unselectable items, would be appropriate.
831 vector<string> available_devices;
833 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
834 available_devices.push_back (i->name);
837 if (!available_devices.empty()) {
839 update_sensitivity ();
842 string current_device, found_device;
843 current_device = device_combo.get_active_text ();
844 if (current_device == "") {
845 current_device = backend->device_name ();
848 // Make sure that the active text is still relevant for this
849 // device (it might only be relevant to the previous device!!)
850 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
851 if (*i == current_device)
852 found_device = current_device;
854 if (found_device == "")
855 // device has never been set (or was not relevant
856 // for this backend) Let's make sure it's not blank
857 current_device = available_devices.front ();
859 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
860 set_popdown_strings (device_combo, available_devices);
862 device_combo.set_active_text (current_device);
867 input_latency.set_sensitive (true);
868 output_latency.set_sensitive (true);
869 input_channels.set_sensitive (true);
870 output_channels.set_sensitive (true);
872 ok_button->set_sensitive (true);
873 apply_button->set_sensitive (true);
876 device_combo.clear();
877 sample_rate_combo.set_sensitive (false);
878 buffer_size_combo.set_sensitive (false);
879 input_latency.set_sensitive (false);
880 output_latency.set_sensitive (false);
881 input_channels.set_sensitive (false);
882 output_channels.set_sensitive (false);
884 ok_button->set_sensitive (false);
885 apply_button->set_sensitive (false);
887 ok_button->set_sensitive (true);
888 apply_button->set_sensitive (true);
889 if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
890 sample_rate_combo.set_sensitive (true);
892 if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
893 buffer_size_combo.set_sensitive (true);
901 EngineControl::driver_changed ()
903 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
906 backend->set_driver (driver_combo.get_active_text());
909 if (!ignore_changes) {
910 maybe_display_saved_state ();
915 EngineControl::device_changed ()
918 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
920 string device_name = device_combo.get_active_text ();
923 //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
924 backend->set_device_name(device_name);
927 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
929 /* don't allow programmatic change to combos to cause a
930 recursive call to this method.
940 sr = backend->available_sample_rates (device_name);
943 sr.push_back (8000.0f);
944 sr.push_back (16000.0f);
945 sr.push_back (32000.0f);
946 sr.push_back (44100.0f);
947 sr.push_back (48000.0f);
948 sr.push_back (88200.0f);
949 sr.push_back (96000.0f);
950 sr.push_back (192000.0f);
951 sr.push_back (384000.0f);
954 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
955 s.push_back (rate_as_string (*x));
956 if (*x == _desired_sample_rate) {
962 sample_rate_combo.set_sensitive (true);
963 set_popdown_strings (sample_rate_combo, s);
965 if (desired.empty()) {
966 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
968 sample_rate_combo.set_active_text (desired);
972 sample_rate_combo.set_sensitive (false);
980 bs = backend->available_buffer_sizes (device_name);
981 } else if (backend->can_change_buffer_size_when_running()) {
995 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
996 s.push_back (bufsize_as_string (*x));
1000 buffer_size_combo.set_sensitive (true);
1001 set_popdown_strings (buffer_size_combo, s);
1003 uint32_t period = backend->buffer_size();
1005 period = backend->default_buffer_size(device_name);
1007 buffer_size_combo.set_active_text (bufsize_as_string (period));
1008 show_buffer_duration ();
1010 buffer_size_combo.set_sensitive (false);
1013 /* XXX theoretically need to set min + max channel counts here
1016 manage_control_app_sensitivity ();
1019 /* pick up any saved state for this device */
1021 if (!ignore_changes) {
1022 maybe_display_saved_state ();
1027 EngineControl::bufsize_as_string (uint32_t sz)
1029 /* Translators: "samples" is always plural here, so no
1030 need for plural+singular forms.
1033 snprintf (buf, sizeof (buf), _("%u samples"), sz);
1038 EngineControl::sample_rate_changed ()
1040 /* reset the strings for buffer size to show the correct msec value
1041 (reflecting the new sample rate).
1044 show_buffer_duration ();
1045 if (!ignore_changes) {
1052 EngineControl::buffer_size_changed ()
1054 show_buffer_duration ();
1055 if (!ignore_changes) {
1061 EngineControl::show_buffer_duration ()
1064 /* buffer sizes - convert from just samples to samples + msecs for
1065 * the displayed string
1068 string bs_text = buffer_size_combo.get_active_text ();
1069 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1070 uint32_t rate = get_rate();
1072 /* Developers: note the hard-coding of a double buffered model
1073 in the (2 * samples) computation of latency. we always start
1074 the audiobackend in this configuration.
1076 /* note to jack1 developers: ardour also always starts the engine
1077 * in async mode (no jack2 --sync option) which adds an extra cycle
1078 * of latency with jack2 (and *3 would be correct)
1079 * The value can also be wrong if jackd is started externally..
1081 * At the time of writing the ALSA backend always uses double-buffering *2,
1082 * The Dummy backend *1, and who knows what ASIO really does :)
1084 * So just display the period size, that's also what
1085 * ARDOUR_UI::update_sample_rate() does for the status bar.
1086 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1087 * but still, that's the buffer period, not [round-trip] latency)
1090 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1091 buffer_size_duration_label.set_text (buf);
1095 EngineControl::midi_option_changed ()
1097 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1100 backend->set_midi_option (get_midi_option());
1102 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1104 //_midi_devices.clear(); // TODO merge with state-saved settings..
1105 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1106 std::vector<MidiDeviceSettings> new_devices;
1108 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1109 MidiDeviceSettings mds = find_midi_device (i->name);
1110 if (i->available && !mds) {
1111 uint32_t input_latency = 0;
1112 uint32_t output_latency = 0;
1113 if (_can_set_midi_latencies) {
1114 input_latency = backend->systemic_midi_input_latency (i->name);
1115 output_latency = backend->systemic_midi_output_latency (i->name);
1117 bool enabled = backend->midi_device_enabled (i->name);
1118 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1119 new_devices.push_back (ptr);
1120 } else if (i->available) {
1121 new_devices.push_back (mds);
1124 _midi_devices = new_devices;
1126 if (_midi_devices.empty()) {
1127 midi_devices_button.set_sensitive (false);
1129 midi_devices_button.set_sensitive (true);
1132 if (!ignore_changes) {
1138 EngineControl::parameter_changed ()
1140 if (!ignore_changes) {
1145 EngineControl::State
1146 EngineControl::get_matching_state (
1147 const string& backend,
1148 const string& driver,
1149 const string& device)
1151 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1152 if ((*i)->backend == backend &&
1153 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1161 EngineControl::State
1162 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1164 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1167 return get_matching_state (backend_combo.get_active_text(),
1168 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1169 device_combo.get_active_text());
1173 return get_matching_state (backend_combo.get_active_text(),
1175 device_combo.get_active_text());
1178 EngineControl::State
1179 EngineControl::save_state ()
1183 if (!_have_control) {
1184 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1188 state.reset(new StateStruct);
1189 state->backend = get_backend ();
1191 state.reset(new StateStruct);
1192 store_state (state);
1195 for (StateList::iterator i = states.begin(); i != states.end();) {
1196 if ((*i)->backend == state->backend &&
1197 (*i)->driver == state->driver &&
1198 (*i)->device == state->device) {
1199 i = states.erase(i);
1205 states.push_back (state);
1211 EngineControl::store_state (State state)
1213 state->backend = get_backend ();
1214 state->driver = get_driver ();
1215 state->device = get_device_name ();
1216 state->sample_rate = get_rate ();
1217 state->buffer_size = get_buffer_size ();
1218 state->input_latency = get_input_latency ();
1219 state->output_latency = get_output_latency ();
1220 state->input_channels = get_input_channels ();
1221 state->output_channels = get_output_channels ();
1222 state->midi_option = get_midi_option ();
1223 state->midi_devices = _midi_devices;
1227 EngineControl::maybe_display_saved_state ()
1229 if (!_have_control) {
1233 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1236 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1238 if (!_desired_sample_rate) {
1239 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1241 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
1242 /* call this explicitly because we're ignoring changes to
1243 the controls at this point.
1245 show_buffer_duration ();
1246 input_latency.set_value (state->input_latency);
1247 output_latency.set_value (state->output_latency);
1249 if (!state->midi_option.empty()) {
1250 midi_option_combo.set_active_text (state->midi_option);
1251 _midi_devices = state->midi_devices;
1257 EngineControl::get_state ()
1259 XMLNode* root = new XMLNode ("AudioMIDISetup");
1262 if (!states.empty()) {
1263 XMLNode* state_nodes = new XMLNode ("EngineStates");
1265 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1267 XMLNode* node = new XMLNode ("State");
1269 node->add_property ("backend", (*i)->backend);
1270 node->add_property ("driver", (*i)->driver);
1271 node->add_property ("device", (*i)->device);
1272 node->add_property ("sample-rate", (*i)->sample_rate);
1273 node->add_property ("buffer-size", (*i)->buffer_size);
1274 node->add_property ("input-latency", (*i)->input_latency);
1275 node->add_property ("output-latency", (*i)->output_latency);
1276 node->add_property ("input-channels", (*i)->input_channels);
1277 node->add_property ("output-channels", (*i)->output_channels);
1278 node->add_property ("active", (*i)->active ? "yes" : "no");
1279 node->add_property ("midi-option", (*i)->midi_option);
1281 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1282 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1283 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1284 midi_device_stuff->add_property (X_("name"), (*p)->name);
1285 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1286 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1287 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1288 midi_devices->add_child_nocopy (*midi_device_stuff);
1290 node->add_child_nocopy (*midi_devices);
1292 state_nodes->add_child_nocopy (*node);
1295 root->add_child_nocopy (*state_nodes);
1302 EngineControl::set_state (const XMLNode& root)
1304 XMLNodeList clist, cclist;
1305 XMLNodeConstIterator citer, cciter;
1307 XMLNode* grandchild;
1308 XMLProperty* prop = NULL;
1310 if (root.name() != "AudioMIDISetup") {
1314 clist = root.children();
1318 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1322 if (child->name() != "EngineStates") {
1326 cclist = child->children();
1328 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1329 State state (new StateStruct);
1331 grandchild = *cciter;
1333 if (grandchild->name() != "State") {
1337 if ((prop = grandchild->property ("backend")) == 0) {
1340 state->backend = prop->value ();
1342 if ((prop = grandchild->property ("driver")) == 0) {
1345 state->driver = prop->value ();
1347 if ((prop = grandchild->property ("device")) == 0) {
1350 state->device = prop->value ();
1352 if ((prop = grandchild->property ("sample-rate")) == 0) {
1355 state->sample_rate = atof (prop->value ());
1357 if ((prop = grandchild->property ("buffer-size")) == 0) {
1360 state->buffer_size = atoi (prop->value ());
1362 if ((prop = grandchild->property ("input-latency")) == 0) {
1365 state->input_latency = atoi (prop->value ());
1367 if ((prop = grandchild->property ("output-latency")) == 0) {
1370 state->output_latency = atoi (prop->value ());
1372 if ((prop = grandchild->property ("input-channels")) == 0) {
1375 state->input_channels = atoi (prop->value ());
1377 if ((prop = grandchild->property ("output-channels")) == 0) {
1380 state->output_channels = atoi (prop->value ());
1382 if ((prop = grandchild->property ("active")) == 0) {
1385 state->active = string_is_affirmative (prop->value ());
1387 if ((prop = grandchild->property ("midi-option")) == 0) {
1390 state->midi_option = prop->value ();
1392 state->midi_devices.clear();
1394 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1395 const XMLNodeList mnc = midinode->children();
1396 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1397 if ((*n)->property (X_("name")) == 0
1398 || (*n)->property (X_("enabled")) == 0
1399 || (*n)->property (X_("input-latency")) == 0
1400 || (*n)->property (X_("output-latency")) == 0
1405 MidiDeviceSettings ptr (new MidiDeviceSetting(
1406 (*n)->property (X_("name"))->value (),
1407 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1408 atoi ((*n)->property (X_("input-latency"))->value ()),
1409 atoi ((*n)->property (X_("output-latency"))->value ())
1411 state->midi_devices.push_back (ptr);
1416 /* remove accumulated duplicates (due to bug in ealier version)
1417 * this can be removed again before release
1419 for (StateList::iterator i = states.begin(); i != states.end();) {
1420 if ((*i)->backend == state->backend &&
1421 (*i)->driver == state->driver &&
1422 (*i)->device == state->device) {
1423 i = states.erase(i);
1430 states.push_back (state);
1434 /* now see if there was an active state and switch the setup to it */
1436 // purge states of backend that are not available in this built
1437 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1438 vector<std::string> backend_names;
1440 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1441 backend_names.push_back((*i)->name);
1443 for (StateList::iterator i = states.begin(); i != states.end();) {
1444 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1445 i = states.erase(i);
1451 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1455 backend_combo.set_active_text ((*i)->backend);
1456 driver_combo.set_active_text ((*i)->driver);
1457 device_combo.set_active_text ((*i)->device);
1458 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1459 buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
1460 input_latency.set_value ((*i)->input_latency);
1461 output_latency.set_value ((*i)->output_latency);
1462 midi_option_combo.set_active_text ((*i)->midi_option);
1470 EngineControl::push_state_to_backend (bool start)
1472 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1478 /* figure out what is going to change */
1480 bool restart_required = false;
1481 bool was_running = ARDOUR::AudioEngine::instance()->running();
1482 bool change_driver = false;
1483 bool change_device = false;
1484 bool change_rate = false;
1485 bool change_bufsize = false;
1486 bool change_latency = false;
1487 bool change_channels = false;
1488 bool change_midi = false;
1490 uint32_t ochan = get_output_channels ();
1491 uint32_t ichan = get_input_channels ();
1493 if (_have_control) {
1495 if (started_at_least_once) {
1497 /* we can control the backend */
1499 if (backend->requires_driver_selection()) {
1500 if (get_driver() != backend->driver_name()) {
1501 change_driver = true;
1505 if (get_device_name() != backend->device_name()) {
1506 change_device = true;
1509 if (get_rate() != backend->sample_rate()) {
1513 if (get_buffer_size() != backend->buffer_size()) {
1514 change_bufsize = true;
1517 if (get_midi_option() != backend->midi_option()) {
1521 /* zero-requested channels means "all available" */
1524 ichan = backend->input_channels();
1528 ochan = backend->output_channels();
1531 if (ichan != backend->input_channels()) {
1532 change_channels = true;
1535 if (ochan != backend->output_channels()) {
1536 change_channels = true;
1539 if (get_input_latency() != backend->systemic_input_latency() ||
1540 get_output_latency() != backend->systemic_output_latency()) {
1541 change_latency = true;
1544 /* backend never started, so we have to force a group
1547 change_device = true;
1548 if (backend->requires_driver_selection()) {
1549 change_driver = true;
1552 change_bufsize = true;
1553 change_channels = true;
1554 change_latency = true;
1560 /* we have no control over the backend, meaning that we can
1561 * only possibly change sample rate and buffer size.
1565 if (get_rate() != backend->sample_rate()) {
1566 change_bufsize = true;
1569 if (get_buffer_size() != backend->buffer_size()) {
1570 change_bufsize = true;
1574 if (!_have_control) {
1576 /* We do not have control over the backend, so the best we can
1577 * do is try to change the sample rate and/or bufsize and get
1581 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1585 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1590 backend->set_sample_rate (get_rate());
1593 if (change_bufsize) {
1594 backend->set_buffer_size (get_buffer_size());
1598 if (ARDOUR::AudioEngine::instance()->start ()) {
1599 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1609 /* determine if we need to stop the backend before changing parameters */
1611 if (change_driver || change_device || change_channels || change_latency ||
1612 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1614 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1615 restart_required = true;
1617 restart_required = false;
1622 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1623 /* no changes in any parameters that absolutely require a
1624 * restart, so check those that might be changeable without a
1628 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1629 /* can't do this while running ... */
1630 restart_required = true;
1633 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1634 /* can't do this while running ... */
1635 restart_required = true;
1641 if (restart_required) {
1642 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1649 if (change_driver && backend->set_driver (get_driver())) {
1650 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1653 if (change_device && backend->set_device_name (get_device_name())) {
1654 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1657 if (change_rate && backend->set_sample_rate (get_rate())) {
1658 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1661 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1662 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1666 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1667 if (backend->set_input_channels (get_input_channels())) {
1668 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1671 if (backend->set_output_channels (get_output_channels())) {
1672 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1676 if (change_latency) {
1677 if (backend->set_systemic_input_latency (get_input_latency())) {
1678 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1681 if (backend->set_systemic_output_latency (get_output_latency())) {
1682 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1688 backend->set_midi_option (get_midi_option());
1692 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1693 if (_measure_midi) {
1694 if (*p == _measure_midi) {
1695 backend->set_midi_device_enabled ((*p)->name, true);
1697 backend->set_midi_device_enabled ((*p)->name, false);
1701 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1702 if (backend->can_set_systemic_midi_latencies()) {
1703 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1704 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1709 if (start || (was_running && restart_required)) {
1710 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1721 EngineControl::post_push ()
1723 /* get a pointer to the current state object, creating one if
1727 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1730 state = save_state ();
1736 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1737 (*i)->active = false;
1740 /* mark this one active (to be used next time the dialog is
1744 state->active = true;
1746 if (_have_control) { // XXX
1747 manage_control_app_sensitivity ();
1750 /* schedule a redisplay of MIDI ports */
1751 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1756 EngineControl::get_rate () const
1758 float r = atof (sample_rate_combo.get_active_text ());
1759 /* the string may have been translated with an abbreviation for
1760 * thousands, so use a crude heuristic to fix this.
1770 EngineControl::get_buffer_size () const
1772 string txt = buffer_size_combo.get_active_text ();
1775 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1783 EngineControl::get_midi_option () const
1785 return midi_option_combo.get_active_text();
1789 EngineControl::get_input_channels() const
1791 if (ARDOUR::Profile->get_mixbus()) {
1792 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1793 if (!backend) return 0;
1794 return backend->input_channels();
1796 return (uint32_t) input_channels_adjustment.get_value();
1800 EngineControl::get_output_channels() const
1802 if (ARDOUR::Profile->get_mixbus()) {
1803 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1804 if (!backend) return 0;
1805 return backend->input_channels();
1807 return (uint32_t) output_channels_adjustment.get_value();
1811 EngineControl::get_input_latency() const
1813 return (uint32_t) input_latency_adjustment.get_value();
1817 EngineControl::get_output_latency() const
1819 return (uint32_t) output_latency_adjustment.get_value();
1823 EngineControl::get_backend () const
1825 return backend_combo.get_active_text ();
1829 EngineControl::get_driver () const
1831 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1832 return driver_combo.get_active_text ();
1839 EngineControl::get_device_name () const
1841 return device_combo.get_active_text ();
1845 EngineControl::control_app_button_clicked ()
1847 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1853 backend->launch_control_app ();
1857 EngineControl::manage_control_app_sensitivity ()
1859 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1865 string appname = backend->control_app_name();
1867 if (appname.empty()) {
1868 control_app_button.set_sensitive (false);
1870 control_app_button.set_sensitive (true);
1875 EngineControl::set_desired_sample_rate (uint32_t sr)
1877 _desired_sample_rate = sr;
1882 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1884 if (page_num == 0) {
1885 cancel_button->set_sensitive (true);
1886 ok_button->set_sensitive (true);
1887 apply_button->set_sensitive (true);
1888 _measure_midi.reset();
1890 cancel_button->set_sensitive (false);
1891 ok_button->set_sensitive (false);
1892 apply_button->set_sensitive (false);
1895 if (page_num == midi_tab) {
1897 refresh_midi_display ();
1900 if (page_num == latency_tab) {
1903 if (ARDOUR::AudioEngine::instance()->running()) {
1904 // TODO - mark as 'stopped for latency
1905 ARDOUR_UI::instance()->disconnect_from_engine ();
1909 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1911 /* save any existing latency values */
1913 uint32_t il = (uint32_t) input_latency.get_value ();
1914 uint32_t ol = (uint32_t) input_latency.get_value ();
1916 /* reset to zero so that our new test instance
1917 will be clean of any existing latency measures.
1919 NB. this should really be done by the backend
1920 when stated for latency measurement.
1923 input_latency.set_value (0);
1924 output_latency.set_value (0);
1926 push_state_to_backend (false);
1930 input_latency.set_value (il);
1931 output_latency.set_value (ol);
1934 // This should be done in push_state_to_backend()
1935 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1936 disable_latency_tab ();
1939 enable_latency_tab ();
1943 end_latency_detection ();
1944 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1949 /* latency measurement */
1952 EngineControl::check_audio_latency_measurement ()
1954 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1956 if (mtdm->resolve () < 0) {
1957 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1961 if (mtdm->err () > 0.3) {
1967 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1969 if (sample_rate == 0) {
1970 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1971 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1975 int frames_total = mtdm->del();
1976 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1978 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1979 _("Detected roundtrip latency: "),
1980 frames_total, frames_total * 1000.0f/sample_rate,
1981 _("Systemic latency: "),
1982 extra, extra * 1000.0f/sample_rate);
1986 if (mtdm->err () > 0.2) {
1988 strcat (buf, _("(signal detection error)"));
1994 strcat (buf, _("(inverted - bad wiring)"));
1998 lm_results.set_markup (string_compose (results_markup, buf));
2001 have_lm_results = true;
2002 end_latency_detection ();
2003 lm_use_button.set_sensitive (true);
2011 EngineControl::check_midi_latency_measurement ()
2013 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2015 if (!mididm->have_signal () || mididm->latency () == 0) {
2016 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2021 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2023 if (sample_rate == 0) {
2024 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2025 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2029 ARDOUR::framecnt_t frames_total = mididm->latency();
2030 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2031 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2032 _("Detected roundtrip latency: "),
2033 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2034 _("Systemic latency: "),
2035 extra, extra * 1000.0f / sample_rate);
2039 if (!mididm->ok ()) {
2041 strcat (buf, _("(averaging)"));
2045 if (mididm->deviation () > 50.0) {
2047 strcat (buf, _("(too large jitter)"));
2049 } else if (mididm->deviation () > 10.0) {
2051 strcat (buf, _("(large jitter)"));
2055 have_lm_results = true;
2056 end_latency_detection ();
2057 lm_use_button.set_sensitive (true);
2058 lm_results.set_markup (string_compose (results_markup, buf));
2060 } else if (mididm->processed () > 400) {
2061 have_lm_results = false;
2062 end_latency_detection ();
2063 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2067 lm_results.set_markup (string_compose (results_markup, buf));
2073 EngineControl::start_latency_detection ()
2075 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2076 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2078 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2079 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2080 if (_measure_midi) {
2081 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2083 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2085 lm_measure_label.set_text (_("Cancel"));
2086 have_lm_results = false;
2087 lm_use_button.set_sensitive (false);
2088 lm_input_channel_combo.set_sensitive (false);
2089 lm_output_channel_combo.set_sensitive (false);
2095 EngineControl::end_latency_detection ()
2097 latency_timeout.disconnect ();
2098 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2099 lm_measure_label.set_text (_("Measure"));
2100 if (!have_lm_results) {
2101 lm_use_button.set_sensitive (false);
2103 lm_input_channel_combo.set_sensitive (true);
2104 lm_output_channel_combo.set_sensitive (true);
2109 EngineControl::latency_button_clicked ()
2112 start_latency_detection ();
2114 end_latency_detection ();
2119 EngineControl::use_latency_button_clicked ()
2121 if (_measure_midi) {
2122 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2126 ARDOUR::framecnt_t frames_total = mididm->latency();
2127 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2128 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2129 _measure_midi->input_latency = one_way;
2130 _measure_midi->output_latency = one_way;
2131 notebook.set_current_page (midi_tab);
2133 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2139 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2140 one_way = std::max (0., one_way);
2142 input_latency_adjustment.set_value (one_way);
2143 output_latency_adjustment.set_value (one_way);
2145 /* back to settings page */
2146 notebook.set_current_page (0);
2152 EngineControl::on_delete_event (GdkEventAny* ev)
2154 if (notebook.get_current_page() == 2) {
2155 /* currently on latency tab - be sure to clean up */
2156 end_latency_detection ();
2158 return ArdourDialog::on_delete_event (ev);
2162 EngineControl::engine_running ()
2164 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2167 buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
2168 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2170 buffer_size_combo.set_sensitive (true);
2171 sample_rate_combo.set_sensitive (true);
2173 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2174 connect_disconnect_button.show();
2176 started_at_least_once = true;
2177 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2181 EngineControl::engine_stopped ()
2183 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2186 buffer_size_combo.set_sensitive (false);
2187 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2188 connect_disconnect_button.show();
2190 sample_rate_combo.set_sensitive (true);
2191 buffer_size_combo.set_sensitive (true);
2192 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2196 EngineControl::device_list_changed ()
2198 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
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);