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"
53 #include "ardour_ui.h"
54 #include "engine_dialog.h"
55 #include "gui_thread.h"
61 using namespace Gtkmm2ext;
64 using namespace ARDOUR_UI_UTILS;
66 #define DEBUG_ECONTROL(msg) DEBUG_TRACE (PBD::DEBUG::EngineControl, string_compose ("%1: %2\n", __LINE__, msg));
68 static const unsigned int midi_tab = 2;
69 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
71 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
73 EngineControl::EngineControl ()
74 : ArdourDialog (_("Audio/MIDI Setup"))
77 , input_latency_adjustment (0, 0, 99999, 1)
78 , input_latency (input_latency_adjustment)
79 , output_latency_adjustment (0, 0, 99999, 1)
80 , output_latency (output_latency_adjustment)
81 , input_channels_adjustment (0, 0, 256, 1)
82 , input_channels (input_channels_adjustment)
83 , output_channels_adjustment (0, 0, 256, 1)
84 , output_channels (output_channels_adjustment)
85 , ports_adjustment (128, 8, 1024, 1, 16)
86 , ports_spinner (ports_adjustment)
87 , control_app_button (_("Device Control Panel"))
88 , midi_devices_button (_("Midi Device Setup"))
89 , lm_measure_label (_("Measure"))
90 , lm_use_button (_("Use results"))
91 , lm_back_button (_("Back to settings ... (ignore results)"))
92 , lm_button_audio (_("Calibrate Audio"))
94 , have_lm_results (false)
96 , midi_back_button (_("Back to settings"))
98 , _desired_sample_rate (0)
99 , started_at_least_once (false)
100 , queue_device_changed (false)
103 using namespace Notebook_Helpers;
104 vector<string> backend_names;
106 AttachOptions xopt = AttachOptions (FILL|EXPAND);
109 set_name (X_("AudioMIDISetup"));
111 /* the backend combo is the one thing that is ALWAYS visible */
113 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
115 if (backends.empty()) {
116 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));
118 throw failed_constructor ();
121 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
122 backend_names.push_back ((*b)->name);
125 set_popdown_strings (backend_combo, backend_names);
127 /* setup basic packing characteristics for the table used on the main
128 * tab of the notebook
131 basic_packer.set_spacings (6);
132 basic_packer.set_border_width (12);
133 basic_packer.set_homogeneous (false);
137 basic_hbox.pack_start (basic_packer, false, false);
139 /* latency measurement tab */
141 lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
144 lm_table.set_row_spacings (12);
145 lm_table.set_col_spacings (6);
146 lm_table.set_homogeneous (false);
148 lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
151 lm_preamble.set_width_chars (60);
152 lm_preamble.set_line_wrap (true);
153 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
155 lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
158 Gtk::Label* preamble;
159 preamble = manage (new Label);
160 preamble->set_width_chars (60);
161 preamble->set_line_wrap (true);
162 preamble->set_markup (_("Select two channels below and connect them using a cable."));
164 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
167 label = manage (new Label (_("Output channel")));
168 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
170 Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
171 misc_align->add (lm_output_channel_combo);
172 lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
175 label = manage (new Label (_("Input channel")));
176 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
178 misc_align = manage (new Alignment (0.0, 0.5));
179 misc_align->add (lm_input_channel_combo);
180 lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
183 lm_measure_label.set_padding (10, 10);
184 lm_measure_button.add (lm_measure_label);
185 lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
186 lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
187 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
189 lm_use_button.set_sensitive (false);
191 /* Increase the default spacing around the labels of these three
197 if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
198 l->set_padding (10, 10);
201 if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
202 l->set_padding (10, 10);
205 preamble = manage (new Label);
206 preamble->set_width_chars (60);
207 preamble->set_line_wrap (true);
208 preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
209 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
212 preamble = manage (new Label);
213 preamble->set_width_chars (60);
214 preamble->set_line_wrap (true);
215 preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
216 lm_table.attach (*preamble, 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_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
223 ++row; // skip a row in the table
224 ++row; // skip a row in the table
226 lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
227 lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
228 lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
230 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
232 lm_vbox.set_border_width (12);
233 lm_vbox.pack_start (lm_table, false, false);
235 midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
239 notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
240 notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
241 notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
242 notebook.set_border_width (12);
244 notebook.set_show_tabs (false);
245 notebook.show_all ();
247 notebook.set_name ("SettingsNotebook");
249 /* packup the notebook */
251 get_vbox()->set_border_width (12);
252 get_vbox()->pack_start (notebook);
254 get_action_area()->pack_start (engine_status);
255 engine_status.show();
257 /* need a special function to print "all available channels" when the
258 * channel counts hit zero.
261 input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
262 output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
264 midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
265 midi_devices_button.set_sensitive (false);
266 midi_devices_button.set_name ("generic button");
267 midi_devices_button.set_can_focus(true);
269 control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
270 manage_control_app_sensitivity ();
272 cancel_button = add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CANCEL);
273 apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
274 ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
276 /* Pick up any existing audio setup configuration, if appropriate */
278 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
280 ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
281 ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
282 ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
283 ARDOUR::AudioEngine::instance()->DeviceListChanged.connect (devicelist_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::device_list_changed, this), gui_context());
286 set_state (*audio_setup);
289 if (backend_combo.get_active_text().empty()) {
290 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
291 backend_combo.set_active_text (backend_names.front());
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);
304 connect_changed_signals ();
306 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
308 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
309 connect_disconnect_button.set_no_show_all();
314 EngineControl::connect_changed_signals ()
316 backend_combo_connection = backend_combo.signal_changed ().connect (
317 sigc::mem_fun (*this, &EngineControl::backend_changed));
318 driver_combo_connection = driver_combo.signal_changed ().connect (
319 sigc::mem_fun (*this, &EngineControl::driver_changed));
320 sample_rate_combo_connection = sample_rate_combo.signal_changed ().connect (
321 sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
322 buffer_size_combo_connection = buffer_size_combo.signal_changed ().connect (
323 sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
324 device_combo_connection = device_combo.signal_changed ().connect (
325 sigc::mem_fun (*this, &EngineControl::device_changed));
326 midi_option_combo_connection = midi_option_combo.signal_changed ().connect (
327 sigc::mem_fun (*this, &EngineControl::midi_option_changed));
329 input_device_combo_connection = input_device_combo.signal_changed ().connect (
330 sigc::mem_fun (*this, &EngineControl::input_device_changed));
331 output_device_combo_connection = output_device_combo.signal_changed ().connect (
332 sigc::mem_fun (*this, &EngineControl::output_device_changed));
334 input_latency_connection = input_latency.signal_changed ().connect (
335 sigc::mem_fun (*this, &EngineControl::parameter_changed));
336 output_latency_connection = output_latency.signal_changed ().connect (
337 sigc::mem_fun (*this, &EngineControl::parameter_changed));
338 input_channels_connection = input_channels.signal_changed ().connect (
339 sigc::mem_fun (*this, &EngineControl::parameter_changed));
340 output_channels_connection = output_channels.signal_changed ().connect (
341 sigc::mem_fun (*this, &EngineControl::parameter_changed));
345 EngineControl::block_changed_signals ()
347 if (block_signals++ == 0) {
348 backend_combo_connection.block ();
349 driver_combo_connection.block ();
350 sample_rate_combo_connection.block ();
351 buffer_size_combo_connection.block ();
352 device_combo_connection.block ();
353 input_device_combo_connection.block ();
354 output_device_combo_connection.block ();
355 midi_option_combo_connection.block ();
356 input_latency_connection.block ();
357 output_latency_connection.block ();
358 input_channels_connection.block ();
359 output_channels_connection.block ();
364 EngineControl::unblock_changed_signals ()
366 if (--block_signals == 0) {
367 backend_combo_connection.unblock ();
368 driver_combo_connection.unblock ();
369 sample_rate_combo_connection.unblock ();
370 buffer_size_combo_connection.unblock ();
371 device_combo_connection.unblock ();
372 input_device_combo_connection.unblock ();
373 output_device_combo_connection.unblock ();
374 midi_option_combo_connection.unblock ();
375 input_latency_connection.unblock ();
376 output_latency_connection.unblock ();
377 input_channels_connection.unblock ();
378 output_channels_connection.unblock ();
382 EngineControl::SignalBlocker::SignalBlocker (EngineControl& engine_control,
383 const std::string& reason)
384 : ec (engine_control)
387 DEBUG_ECONTROL (string_compose ("SignalBlocker: %1", m_reason));
388 ec.block_changed_signals ();
391 EngineControl::SignalBlocker::~SignalBlocker ()
393 DEBUG_ECONTROL (string_compose ("~SignalBlocker: %1", m_reason));
394 ec.unblock_changed_signals ();
398 EngineControl::on_show ()
400 ArdourDialog::on_show ();
401 if (!ARDOUR::AudioEngine::instance()->current_backend() || !ARDOUR::AudioEngine::instance()->running()) {
402 // re-check _have_control (jackd running) see #6041
406 ok_button->grab_focus();
410 EngineControl::on_response (int response_id)
412 ArdourDialog::on_response (response_id);
414 switch (response_id) {
416 push_state_to_backend (true);
419 #ifdef PLATFORM_WINDOWS
420 // For some reason we don't understand, 'hide()'
421 // needs to get called first in Windows
424 // But if there's no session open, this can produce
425 // a long gap when nothing appears to be happening.
426 // Let's show the splash image while we're waiting.
427 if ( !ARDOUR_COMMAND_LINE::no_splash ) {
428 if ( ARDOUR_UI::instance() ) {
429 if ( !ARDOUR_UI::instance()->session_loaded ) {
430 ARDOUR_UI::instance()->show_splash();
434 push_state_to_backend (true);
437 push_state_to_backend (true);
441 case RESPONSE_DELETE_EVENT:
444 ev.type = GDK_BUTTON_PRESS;
446 on_delete_event ((GdkEventAny*) &ev);
455 EngineControl::build_notebook ()
458 AttachOptions xopt = AttachOptions (FILL|EXPAND);
460 /* clear the table */
462 Gtkmm2ext::container_clear (basic_vbox);
463 Gtkmm2ext::container_clear (basic_packer);
465 if (control_app_button.get_parent()) {
466 control_app_button.get_parent()->remove (control_app_button);
469 label = manage (left_aligned_label (_("Audio System:")));
470 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
471 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
473 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
474 lm_button_audio.set_name ("generic button");
475 lm_button_audio.set_can_focus(true);
478 build_full_control_notebook ();
480 build_no_control_notebook ();
483 basic_vbox.pack_start (basic_hbox, false, false);
486 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
487 basic_vbox.show_all ();
492 EngineControl::build_full_control_notebook ()
494 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
497 using namespace Notebook_Helpers;
499 vector<string> strings;
500 AttachOptions xopt = AttachOptions (FILL|EXPAND);
501 int row = 1; // row zero == backend combo
503 /* start packing it up */
505 if (backend->requires_driver_selection()) {
506 label = manage (left_aligned_label (_("Driver:")));
507 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
508 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
512 if (backend->use_separate_input_and_output_devices()) {
513 label = manage (left_aligned_label (_("Input Device:")));
514 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
515 basic_packer.attach (input_device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
517 label = manage (left_aligned_label (_("Output Device:")));
518 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
519 basic_packer.attach (output_device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
521 // reset so it isn't used in state comparisons
522 device_combo.set_active_text ("");
524 label = manage (left_aligned_label (_("Device:")));
525 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
526 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
528 // reset these so they don't get used in state comparisons
529 input_device_combo.set_active_text ("");
530 output_device_combo.set_active_text ("");
533 label = manage (left_aligned_label (_("Sample rate:")));
534 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
535 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
539 label = manage (left_aligned_label (_("Buffer size:")));
540 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
541 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
542 buffer_size_duration_label.set_alignment (0.0); /* left-align */
543 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
545 /* button spans 2 rows */
547 basic_packer.attach (control_app_button, 3, 4, row-1, row+1, xopt, xopt);
550 input_channels.set_name ("InputChannels");
551 input_channels.set_flags (Gtk::CAN_FOCUS);
552 input_channels.set_digits (0);
553 input_channels.set_wrap (false);
554 output_channels.set_editable (true);
556 if (!ARDOUR::Profile->get_mixbus()) {
557 label = manage (left_aligned_label (_("Input Channels:")));
558 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
559 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
563 output_channels.set_name ("OutputChannels");
564 output_channels.set_flags (Gtk::CAN_FOCUS);
565 output_channels.set_digits (0);
566 output_channels.set_wrap (false);
567 output_channels.set_editable (true);
569 if (!ARDOUR::Profile->get_mixbus()) {
570 label = manage (left_aligned_label (_("Output Channels:")));
571 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
572 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
576 input_latency.set_name ("InputLatency");
577 input_latency.set_flags (Gtk::CAN_FOCUS);
578 input_latency.set_digits (0);
579 input_latency.set_wrap (false);
580 input_latency.set_editable (true);
582 label = manage (left_aligned_label (_("Hardware input latency:")));
583 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
584 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
585 label = manage (left_aligned_label (_("samples")));
586 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
589 output_latency.set_name ("OutputLatency");
590 output_latency.set_flags (Gtk::CAN_FOCUS);
591 output_latency.set_digits (0);
592 output_latency.set_wrap (false);
593 output_latency.set_editable (true);
595 label = manage (left_aligned_label (_("Hardware output latency:")));
596 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
597 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
598 label = manage (left_aligned_label (_("samples")));
599 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
601 /* button spans 2 rows */
603 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
606 label = manage (left_aligned_label (_("MIDI System:")));
607 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
608 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
609 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
614 EngineControl::build_no_control_notebook ()
616 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
619 using namespace Notebook_Helpers;
621 vector<string> strings;
622 AttachOptions xopt = AttachOptions (FILL|EXPAND);
623 int row = 1; // row zero == backend combo
624 const string msg = string_compose (_("%1 is already running. %2 will connect to it and use the existing settings."), backend->name(), PROGRAM_NAME);
626 label = manage (new Label);
627 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
628 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
631 if (backend->can_change_sample_rate_when_running()) {
632 label = manage (left_aligned_label (_("Sample rate:")));
633 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
634 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
638 if (backend->can_change_buffer_size_when_running()) {
639 label = manage (left_aligned_label (_("Buffer size:")));
640 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
641 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
642 buffer_size_duration_label.set_alignment (0.0); /* left-align */
643 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
647 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
651 EngineControl::~EngineControl ()
653 ignore_changes = true;
657 EngineControl::disable_latency_tab ()
659 vector<string> empty;
660 set_popdown_strings (lm_output_channel_combo, empty);
661 set_popdown_strings (lm_input_channel_combo, empty);
662 lm_measure_button.set_sensitive (false);
663 lm_use_button.set_sensitive (false);
667 EngineControl::enable_latency_tab ()
669 vector<string> outputs;
670 vector<string> inputs;
672 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
673 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
674 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
676 if (!ARDOUR::AudioEngine::instance()->running()) {
677 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
678 notebook.set_current_page (0);
682 else if (inputs.empty() || outputs.empty()) {
683 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
684 notebook.set_current_page (0);
689 lm_back_button_signal.disconnect();
691 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
694 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
698 set_popdown_strings (lm_output_channel_combo, outputs);
699 lm_output_channel_combo.set_active_text (outputs.front());
700 lm_output_channel_combo.set_sensitive (true);
702 set_popdown_strings (lm_input_channel_combo, inputs);
703 lm_input_channel_combo.set_active_text (inputs.front());
704 lm_input_channel_combo.set_sensitive (true);
706 lm_measure_button.set_sensitive (true);
710 EngineControl::setup_midi_tab_for_backend ()
712 string backend = backend_combo.get_active_text ();
714 Gtkmm2ext::container_clear (midi_vbox);
716 midi_vbox.set_border_width (12);
717 midi_device_table.set_border_width (12);
719 if (backend == "JACK") {
720 setup_midi_tab_for_jack ();
723 midi_vbox.pack_start (midi_device_table, true, true);
724 midi_vbox.pack_start (midi_back_button, false, false);
725 midi_vbox.show_all ();
729 EngineControl::setup_midi_tab_for_jack ()
734 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
736 device->input_latency = a->get_value();
738 device->output_latency = a->get_value();
743 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
744 b->set_active (!b->get_active());
745 device->enabled = b->get_active();
746 refresh_midi_display(device->name);
750 EngineControl::refresh_midi_display (std::string focus)
752 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
756 AttachOptions xopt = AttachOptions (FILL|EXPAND);
759 Gtkmm2ext::container_clear (midi_device_table);
761 midi_device_table.set_spacings (6);
763 l = manage (new Label);
764 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
765 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
766 l->set_alignment (0.5, 0.5);
770 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
771 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
772 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
773 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
775 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
776 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
777 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
778 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
781 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
786 bool enabled = (*p)->enabled;
788 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
789 m->set_name ("midi device");
790 m->set_can_focus (Gtk::CAN_FOCUS);
791 m->add_events (Gdk::BUTTON_RELEASE_MASK);
792 m->set_active (enabled);
793 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
794 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
795 if ((*p)->name == focus) {
799 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
800 s = manage (new Gtk::SpinButton (*a));
801 a->set_value ((*p)->input_latency);
802 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
803 s->set_sensitive (_can_set_midi_latencies && enabled);
804 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
806 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
807 s = manage (new Gtk::SpinButton (*a));
808 a->set_value ((*p)->output_latency);
809 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
810 s->set_sensitive (_can_set_midi_latencies && enabled);
811 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
813 b = manage (new Button (_("Calibrate")));
814 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
815 b->set_sensitive (_can_set_midi_latencies && enabled);
816 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
823 EngineControl::backend_changed ()
825 DEBUG_ECONTROL ("backend_changed");
827 string backend_name = backend_combo.get_active_text();
828 boost::shared_ptr<ARDOUR::AudioBackend> backend;
830 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
831 /* eh? setting the backend failed... how ? */
832 /* A: stale config contains a backend that does not exist in current build */
836 DEBUG_ECONTROL (string_compose ("Backend name: %1", backend_name));
838 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
841 setup_midi_tab_for_backend ();
842 _midi_devices.clear();
844 if (backend->requires_driver_selection()) {
845 vector<string> drivers = backend->enumerate_drivers();
846 driver_combo.set_sensitive (true);
848 if (!drivers.empty()) {
850 string current_driver;
851 current_driver = backend->driver_name ();
853 DEBUG_ECONTROL (string_compose ("backend->driver_name: %1", current_driver));
855 // driver might not have been set yet
856 if (current_driver == "") {
857 current_driver = driver_combo.get_active_text ();
858 if (current_driver == "")
859 // driver has never been set, make sure it's not blank
860 current_driver = drivers.front ();
863 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
864 set_popdown_strings (driver_combo, drivers);
866 string_compose ("driver_combo.set_active_text: %1", current_driver));
867 driver_combo.set_active_text (current_driver);
874 driver_combo.set_sensitive (false);
875 /* this will change the device text which will cause a call to
876 * device changed which will set up parameters
881 vector<string> midi_options = backend->enumerate_midi_options();
883 if (midi_options.size() == 1) {
884 /* only contains the "none" option */
885 midi_option_combo.set_sensitive (false);
888 set_popdown_strings (midi_option_combo, midi_options);
889 midi_option_combo.set_active_text (midi_options.front());
890 midi_option_combo.set_sensitive (true);
892 midi_option_combo.set_sensitive (false);
896 connect_disconnect_button.hide();
898 midi_option_changed();
900 started_at_least_once = false;
902 if (!ignore_changes) {
903 maybe_display_saved_state ();
908 EngineControl::print_channel_count (Gtk::SpinButton* sb)
910 if (ARDOUR::Profile->get_mixbus()) {
914 uint32_t cnt = (uint32_t) sb->get_value();
916 sb->set_text (_("all available channels"));
919 snprintf (buf, sizeof (buf), "%d", cnt);
926 EngineControl::set_driver_popdown_strings ()
928 DEBUG_ECONTROL ("set_driver_popdown_strings");
929 string backend_name = backend_combo.get_active_text();
930 boost::shared_ptr<ARDOUR::AudioBackend> backend;
932 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
933 /* eh? setting the backend failed... how ? */
934 /* A: stale config contains a backend that does not exist in current build */
938 vector<string> drivers = backend->enumerate_drivers();
939 set_popdown_strings (driver_combo, drivers);
943 // @return true if there are devices available
945 EngineControl::set_device_popdown_strings ()
947 DEBUG_ECONTROL ("set_device_popdown_strings");
948 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
949 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
951 /* NOTE: Ardour currently does not display the "available" field of the
954 * Doing so would require a different GUI widget than the combo
955 * box/popdown that we currently use, since it has no way to list
956 * items that are not selectable. Something more like a popup menu,
957 * which could have unselectable items, would be appropriate.
960 vector<string> available_devices;
962 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
963 available_devices.push_back (i->name);
967 if (!available_devices.empty()) {
970 string current_device, found_device;
971 current_device = device_combo.get_active_text ();
972 if (current_device == "") {
973 current_device = backend->device_name ();
976 // Make sure that the active text is still relevant for this
977 // device (it might only be relevant to the previous device!!)
978 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
979 if (*i == current_device)
980 found_device = current_device;
982 if (found_device == "")
983 // device has never been set (or was not relevant
984 // for this backend) Let's make sure it's not blank
985 current_device = available_devices.front ();
987 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
988 set_popdown_strings (device_combo, available_devices);
989 DEBUG_ECONTROL (string_compose ("set device_combo active text: %1", current_device));
991 device_combo.set_active_text (current_device);
1000 // @return true if there are input devices available
1002 EngineControl::set_input_device_popdown_strings ()
1004 DEBUG_ECONTROL ("set_input_device_popdown_strings");
1005 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1006 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_input_devices ();
1008 vector<string> available_devices;
1010 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
1011 available_devices.push_back (i->name);
1014 if (!available_devices.empty()) {
1017 string current_device, found_device;
1018 current_device = input_device_combo.get_active_text ();
1019 if (current_device == "") {
1020 current_device = backend->input_device_name ();
1023 // Make sure that the active text is still relevant for this
1024 // device (it might only be relevant to the previous device!!)
1025 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
1026 if (*i == current_device)
1027 found_device = current_device;
1029 if (found_device == "")
1030 // device has never been set (or was not relevant
1031 // for this backend) Let's make sure it's not blank
1032 current_device = available_devices.front ();
1034 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1035 set_popdown_strings (input_device_combo, available_devices);
1037 DEBUG_ECONTROL (string_compose ("set input_device_combo active text: %1", current_device));
1038 input_device_combo.set_active_text (current_device);
1048 // @return true if there are output devices available
1050 EngineControl::set_output_device_popdown_strings ()
1052 DEBUG_ECONTROL ("set_output_device_popdown_strings");
1053 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1054 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_output_devices ();
1056 vector<string> available_devices;
1058 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
1059 available_devices.push_back (i->name);
1062 if (!available_devices.empty()) {
1065 string current_device, found_device;
1066 current_device = output_device_combo.get_active_text ();
1067 if (current_device == "") {
1068 current_device = backend->output_device_name ();
1071 // Make sure that the active text is still relevant for this
1072 // device (it might only be relevant to the previous device!!)
1073 for (vector<string>::const_iterator i = available_devices.begin(); i != available_devices.end(); ++i) {
1074 if (*i == current_device)
1075 found_device = current_device;
1077 if (found_device == "")
1078 // device has never been set (or was not relevant
1079 // for this backend) Let's make sure it's not blank
1080 current_device = available_devices.front ();
1082 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1083 set_popdown_strings (output_device_combo, available_devices);
1085 DEBUG_ECONTROL (string_compose ("set input_device_combo active text: %1", current_device));
1086 output_device_combo.set_active_text (current_device);
1097 EngineControl::list_devices ()
1099 DEBUG_ECONTROL ("list_devices");
1100 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1103 /* now fill out devices, mark sample rates, buffer sizes insensitive */
1105 bool devices_available = false;
1107 if (backend->use_separate_input_and_output_devices ()) {
1108 bool input_devices_available = set_input_device_popdown_strings ();
1109 bool output_devices_available = set_output_device_popdown_strings ();
1110 devices_available = input_devices_available || output_devices_available;
1112 devices_available = set_device_popdown_strings ();
1115 if (devices_available) {
1116 input_latency.set_sensitive (true);
1117 output_latency.set_sensitive (true);
1118 input_channels.set_sensitive (true);
1119 output_channels.set_sensitive (true);
1121 ok_button->set_sensitive (true);
1122 apply_button->set_sensitive (true);
1125 device_combo.clear();
1126 input_device_combo.clear();
1127 output_device_combo.clear();
1128 sample_rate_combo.set_sensitive (false);
1129 buffer_size_combo.set_sensitive (false);
1130 input_latency.set_sensitive (false);
1131 output_latency.set_sensitive (false);
1132 input_channels.set_sensitive (false);
1133 output_channels.set_sensitive (false);
1134 if (_have_control) {
1135 ok_button->set_sensitive (false);
1136 apply_button->set_sensitive (false);
1138 ok_button->set_sensitive (true);
1139 apply_button->set_sensitive (true);
1140 if (backend->can_change_sample_rate_when_running() && sample_rate_combo.get_children().size() > 0) {
1141 sample_rate_combo.set_sensitive (true);
1143 if (backend->can_change_buffer_size_when_running() && buffer_size_combo.get_children().size() > 0) {
1144 buffer_size_combo.set_sensitive (true);
1152 EngineControl::driver_changed ()
1154 DEBUG_ECONTROL ("driver_changed");
1155 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1158 backend->set_driver (driver_combo.get_active_text());
1161 if (!ignore_changes) {
1162 maybe_display_saved_state ();
1167 EngineControl::set_samplerate_popdown_strings (const std::string& device_name)
1169 DEBUG_ECONTROL ("set_samplerate_popdown_strings");
1170 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1175 if (_have_control) {
1176 sr = backend->available_sample_rates (device_name);
1179 sr.push_back (8000.0f);
1180 sr.push_back (16000.0f);
1181 sr.push_back (32000.0f);
1182 sr.push_back (44100.0f);
1183 sr.push_back (48000.0f);
1184 sr.push_back (88200.0f);
1185 sr.push_back (96000.0f);
1186 sr.push_back (192000.0f);
1187 sr.push_back (384000.0f);
1190 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
1191 s.push_back (rate_as_string (*x));
1192 if (*x == _desired_sample_rate) {
1198 sample_rate_combo.set_sensitive (true);
1199 set_popdown_strings (sample_rate_combo, s);
1201 if (desired.empty()) {
1202 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
1204 sample_rate_combo.set_active_text (desired);
1208 sample_rate_combo.set_sensitive (false);
1213 EngineControl::set_buffersize_popdown_strings (const std::string& device_name)
1215 DEBUG_ECONTROL ("set_buffersize_popdown_strings");
1216 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1217 vector<uint32_t> bs;
1220 if (_have_control) {
1221 bs = backend->available_buffer_sizes (device_name);
1222 } else if (backend->can_change_buffer_size_when_running()) {
1230 bs.push_back (1024);
1231 bs.push_back (2048);
1232 bs.push_back (4096);
1233 bs.push_back (8192);
1236 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
1237 s.push_back (bufsize_as_string (*x));
1241 buffer_size_combo.set_sensitive (true);
1242 set_popdown_strings (buffer_size_combo, s);
1243 buffer_size_combo.set_active_text (s.front());
1245 uint32_t period = backend->buffer_size();
1247 period = backend->default_buffer_size(device_name);
1249 set_active_text_if_present (buffer_size_combo, bufsize_as_string (period));
1250 show_buffer_duration ();
1252 buffer_size_combo.set_sensitive (false);
1257 EngineControl::device_changed ()
1259 DEBUG_ECONTROL ("device_changed");
1260 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1263 string device_name_in;
1264 string device_name_out; // only used if backend support separate I/O devices
1266 if (backend->use_separate_input_and_output_devices()) {
1267 device_name_in = get_input_device_name ();
1268 device_name_out = get_output_device_name ();
1270 device_name_in = get_device_name ();
1273 /* we set the backend-device to query various device related intormation.
1274 * This has the side effect that backend->device_name() will match
1275 * the device_name and 'change_device' will never be true.
1276 * so work around this by setting...
1278 if (backend->use_separate_input_and_output_devices()) {
1279 if (device_name_in != backend->input_device_name() || device_name_out != backend->output_device_name ()) {
1280 queue_device_changed = true;
1283 if (device_name_in != backend->device_name()) {
1284 queue_device_changed = true;
1288 //the device name must be set FIRST so ASIO can populate buffersizes and the control panel button
1289 if (backend->use_separate_input_and_output_devices()) {
1290 backend->set_input_device_name (device_name_in);
1291 backend->set_output_device_name (device_name_out);
1293 backend->set_device_name(device_name_in);
1297 /* don't allow programmatic change to combos to cause a
1298 recursive call to this method.
1300 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1302 /* backends that support separate devices, need to ignore
1303 * the device-name - and use the devies set above
1305 set_samplerate_popdown_strings (device_name_in);
1306 set_buffersize_popdown_strings (device_name_in);
1307 /* XXX theoretically need to set min + max channel counts here
1310 manage_control_app_sensitivity ();
1313 /* pick up any saved state for this device */
1315 if (!ignore_changes) {
1316 maybe_display_saved_state ();
1321 EngineControl::input_device_changed ()
1323 DEBUG_ECONTROL ("input_device_changed");
1328 EngineControl::output_device_changed ()
1330 DEBUG_ECONTROL ("output_device_changed");
1335 EngineControl::bufsize_as_string (uint32_t sz)
1337 /* Translators: "samples" is always plural here, so no
1338 need for plural+singular forms.
1341 snprintf (buf, sizeof (buf), "%u %s", sz, P_("sample", "samples", sz));
1346 EngineControl::sample_rate_changed ()
1348 DEBUG_ECONTROL ("sample_rate_changed");
1349 /* reset the strings for buffer size to show the correct msec value
1350 (reflecting the new sample rate).
1353 show_buffer_duration ();
1358 EngineControl::buffer_size_changed ()
1360 DEBUG_ECONTROL ("buffer_size_changed");
1361 show_buffer_duration ();
1365 EngineControl::show_buffer_duration ()
1367 DEBUG_ECONTROL ("show_buffer_duration");
1368 /* buffer sizes - convert from just samples to samples + msecs for
1369 * the displayed string
1372 string bs_text = buffer_size_combo.get_active_text ();
1373 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
1374 uint32_t rate = get_rate();
1376 /* Developers: note the hard-coding of a double buffered model
1377 in the (2 * samples) computation of latency. we always start
1378 the audiobackend in this configuration.
1380 /* note to jack1 developers: ardour also always starts the engine
1381 * in async mode (no jack2 --sync option) which adds an extra cycle
1382 * of latency with jack2 (and *3 would be correct)
1383 * The value can also be wrong if jackd is started externally..
1385 * At the time of writing the ALSA backend always uses double-buffering *2,
1386 * The Dummy backend *1, and who knows what ASIO really does :)
1388 * So just display the period size, that's also what
1389 * ARDOUR_UI::update_sample_rate() does for the status bar.
1390 * (the statusbar calls AudioEngine::instance()->usecs_per_cycle()
1391 * but still, that's the buffer period, not [round-trip] latency)
1394 snprintf (buf, sizeof (buf), _("(%.1f ms)"), (samples / (rate/1000.0f)));
1395 buffer_size_duration_label.set_text (buf);
1399 EngineControl::midi_option_changed ()
1401 DEBUG_ECONTROL ("midi_option_changed");
1402 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1405 backend->set_midi_option (get_midi_option());
1407 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1409 //_midi_devices.clear(); // TODO merge with state-saved settings..
1410 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1411 std::vector<MidiDeviceSettings> new_devices;
1413 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1414 MidiDeviceSettings mds = find_midi_device (i->name);
1415 if (i->available && !mds) {
1416 uint32_t input_latency = 0;
1417 uint32_t output_latency = 0;
1418 if (_can_set_midi_latencies) {
1419 input_latency = backend->systemic_midi_input_latency (i->name);
1420 output_latency = backend->systemic_midi_output_latency (i->name);
1422 bool enabled = backend->midi_device_enabled (i->name);
1423 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1424 new_devices.push_back (ptr);
1425 } else if (i->available) {
1426 new_devices.push_back (mds);
1429 _midi_devices = new_devices;
1431 if (_midi_devices.empty()) {
1432 midi_devices_button.set_sensitive (false);
1434 midi_devices_button.set_sensitive (true);
1439 EngineControl::parameter_changed ()
1443 EngineControl::State
1444 EngineControl::get_matching_state (
1445 const string& backend,
1446 const string& driver,
1447 const string& device)
1449 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1450 if ((*i)->backend == backend &&
1451 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1459 EngineControl::State
1460 EngineControl::get_matching_state (
1461 const string& backend,
1462 const string& driver,
1463 const string& input_device,
1464 const string& output_device)
1466 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1467 if ((*i)->backend == backend &&
1468 (!_have_control || ((*i)->driver == driver && ((*i)->input_device == input_device) && (*i)->output_device == output_device)))
1476 EngineControl::State
1477 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1479 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1482 if (backend->use_separate_input_and_output_devices ()) {
1483 return get_matching_state (backend_combo.get_active_text(),
1484 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1485 input_device_combo.get_active_text(),
1486 output_device_combo.get_active_text());
1488 return get_matching_state (backend_combo.get_active_text(),
1489 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1490 device_combo.get_active_text());
1494 return get_matching_state (backend_combo.get_active_text(),
1496 device_combo.get_active_text());
1499 bool EngineControl::equivalent_states (const EngineControl::State& state1,
1500 const EngineControl::State& state2)
1502 if (state1->backend == state2->backend &&
1503 state1->driver == state2->driver &&
1504 state1->device == state2->device &&
1505 state1->input_device == state2->input_device &&
1506 state1->output_device == state2->output_device) {
1512 EngineControl::State
1513 EngineControl::save_state ()
1517 if (!_have_control) {
1518 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1522 state.reset(new StateStruct);
1523 state->backend = get_backend ();
1525 state.reset(new StateStruct);
1526 store_state (state);
1529 for (StateList::iterator i = states.begin(); i != states.end();) {
1530 if (equivalent_states (*i, state)) {
1531 i = states.erase(i);
1537 states.push_back (state);
1543 EngineControl::store_state (State state)
1545 state->backend = get_backend ();
1546 state->driver = get_driver ();
1547 state->device = get_device_name ();
1548 state->input_device = get_input_device_name ();
1549 state->output_device = get_output_device_name ();
1550 state->sample_rate = get_rate ();
1551 state->buffer_size = get_buffer_size ();
1552 state->input_latency = get_input_latency ();
1553 state->output_latency = get_output_latency ();
1554 state->input_channels = get_input_channels ();
1555 state->output_channels = get_output_channels ();
1556 state->midi_option = get_midi_option ();
1557 state->midi_devices = _midi_devices;
1561 EngineControl::maybe_display_saved_state ()
1563 if (!_have_control) {
1567 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1570 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1572 if (!_desired_sample_rate) {
1573 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1575 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1576 /* call this explicitly because we're ignoring changes to
1577 the controls at this point.
1579 show_buffer_duration ();
1580 input_latency.set_value (state->input_latency);
1581 output_latency.set_value (state->output_latency);
1583 if (!state->midi_option.empty()) {
1584 midi_option_combo.set_active_text (state->midi_option);
1585 _midi_devices = state->midi_devices;
1591 EngineControl::get_state ()
1593 LocaleGuard lg (X_("C"));
1595 XMLNode* root = new XMLNode ("AudioMIDISetup");
1598 if (!states.empty()) {
1599 XMLNode* state_nodes = new XMLNode ("EngineStates");
1601 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1603 XMLNode* node = new XMLNode ("State");
1605 node->add_property ("backend", (*i)->backend);
1606 node->add_property ("driver", (*i)->driver);
1607 node->add_property ("device", (*i)->device);
1608 node->add_property ("input-device", (*i)->input_device);
1609 node->add_property ("output-device", (*i)->output_device);
1610 node->add_property ("sample-rate", (*i)->sample_rate);
1611 node->add_property ("buffer-size", (*i)->buffer_size);
1612 node->add_property ("input-latency", (*i)->input_latency);
1613 node->add_property ("output-latency", (*i)->output_latency);
1614 node->add_property ("input-channels", (*i)->input_channels);
1615 node->add_property ("output-channels", (*i)->output_channels);
1616 node->add_property ("active", (*i)->active ? "yes" : "no");
1617 node->add_property ("midi-option", (*i)->midi_option);
1619 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1620 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1621 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1622 midi_device_stuff->add_property (X_("name"), (*p)->name);
1623 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1624 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1625 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1626 midi_devices->add_child_nocopy (*midi_device_stuff);
1628 node->add_child_nocopy (*midi_devices);
1630 state_nodes->add_child_nocopy (*node);
1633 root->add_child_nocopy (*state_nodes);
1640 EngineControl::set_state (const XMLNode& root)
1642 XMLNodeList clist, cclist;
1643 XMLNodeConstIterator citer, cciter;
1645 XMLNode* grandchild;
1646 XMLProperty* prop = NULL;
1648 fprintf (stderr, "EngineControl::set_state\n");
1650 if (root.name() != "AudioMIDISetup") {
1654 clist = root.children();
1658 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1662 if (child->name() != "EngineStates") {
1666 cclist = child->children();
1668 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1669 State state (new StateStruct);
1671 grandchild = *cciter;
1673 if (grandchild->name() != "State") {
1677 if ((prop = grandchild->property ("backend")) == 0) {
1680 state->backend = prop->value ();
1682 if ((prop = grandchild->property ("driver")) == 0) {
1685 state->driver = prop->value ();
1687 if ((prop = grandchild->property ("device")) == 0) {
1690 state->device = prop->value ();
1692 if ((prop = grandchild->property ("input-device")) == 0) {
1695 state->input_device = prop->value ();
1697 if ((prop = grandchild->property ("output-device")) == 0) {
1700 state->output_device = prop->value ();
1702 if ((prop = grandchild->property ("sample-rate")) == 0) {
1705 state->sample_rate = atof (prop->value ());
1707 if ((prop = grandchild->property ("buffer-size")) == 0) {
1710 state->buffer_size = atoi (prop->value ());
1712 if ((prop = grandchild->property ("input-latency")) == 0) {
1715 state->input_latency = atoi (prop->value ());
1717 if ((prop = grandchild->property ("output-latency")) == 0) {
1720 state->output_latency = atoi (prop->value ());
1722 if ((prop = grandchild->property ("input-channels")) == 0) {
1725 state->input_channels = atoi (prop->value ());
1727 if ((prop = grandchild->property ("output-channels")) == 0) {
1730 state->output_channels = atoi (prop->value ());
1732 if ((prop = grandchild->property ("active")) == 0) {
1735 state->active = string_is_affirmative (prop->value ());
1737 if ((prop = grandchild->property ("midi-option")) == 0) {
1740 state->midi_option = prop->value ();
1742 state->midi_devices.clear();
1744 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1745 const XMLNodeList mnc = midinode->children();
1746 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1747 if ((*n)->property (X_("name")) == 0
1748 || (*n)->property (X_("enabled")) == 0
1749 || (*n)->property (X_("input-latency")) == 0
1750 || (*n)->property (X_("output-latency")) == 0
1755 MidiDeviceSettings ptr (new MidiDeviceSetting(
1756 (*n)->property (X_("name"))->value (),
1757 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1758 atoi ((*n)->property (X_("input-latency"))->value ()),
1759 atoi ((*n)->property (X_("output-latency"))->value ())
1761 state->midi_devices.push_back (ptr);
1766 /* remove accumulated duplicates (due to bug in ealier version)
1767 * this can be removed again before release
1769 for (StateList::iterator i = states.begin(); i != states.end();) {
1770 if ((*i)->backend == state->backend &&
1771 (*i)->driver == state->driver &&
1772 (*i)->device == state->device) {
1773 i = states.erase(i);
1780 states.push_back (state);
1784 /* now see if there was an active state and switch the setup to it */
1786 // purge states of backend that are not available in this built
1787 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1788 vector<std::string> backend_names;
1790 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1791 backend_names.push_back((*i)->name);
1793 for (StateList::iterator i = states.begin(); i != states.end();) {
1794 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1795 i = states.erase(i);
1801 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1804 set_current_state (*i);
1811 EngineControl::set_current_state (const State& state)
1813 DEBUG_ECONTROL ("set_current_state");
1814 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1815 backend_combo.set_active_text (state->backend);
1817 /* The driver popdown strings need to be populated now so that
1818 * set_active_text will actually set a valid entry. Then
1819 * backend_changed() will populate all the other combo's so they
1820 * can also be set to valid entries and the state will be restored
1823 if (!state->driver.empty()) {
1824 set_driver_popdown_strings ();
1826 driver_combo.set_active_text (state->driver);
1829 device_combo.set_active_text (state->device);
1830 input_device_combo.set_active_text (state->input_device);
1831 output_device_combo.set_active_text (state->output_device);
1832 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1833 set_active_text_if_present (buffer_size_combo, bufsize_as_string (state->buffer_size));
1834 input_latency.set_value (state->input_latency);
1835 output_latency.set_value (state->output_latency);
1836 midi_option_combo.set_active_text (state->midi_option);
1840 EngineControl::push_state_to_backend (bool start)
1842 DEBUG_ECONTROL ("push_state_to_backend");
1843 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1849 /* figure out what is going to change */
1851 bool restart_required = false;
1852 bool was_running = ARDOUR::AudioEngine::instance()->running();
1853 bool change_driver = false;
1854 bool change_device = false;
1855 bool change_rate = false;
1856 bool change_bufsize = false;
1857 bool change_latency = false;
1858 bool change_channels = false;
1859 bool change_midi = false;
1861 uint32_t ochan = get_output_channels ();
1862 uint32_t ichan = get_input_channels ();
1864 if (_have_control) {
1866 if (started_at_least_once) {
1868 /* we can control the backend */
1870 if (backend->requires_driver_selection()) {
1871 if (get_driver() != backend->driver_name()) {
1872 change_driver = true;
1876 if (backend->use_separate_input_and_output_devices()) {
1877 if (get_input_device_name() != backend->input_device_name()) {
1878 change_device = true;
1880 if (get_output_device_name() != backend->output_device_name()) {
1881 change_device = true;
1884 if (get_device_name() != backend->device_name()) {
1885 change_device = true;
1889 if (queue_device_changed) {
1890 change_device = true;
1893 if (get_rate() != backend->sample_rate()) {
1897 if (get_buffer_size() != backend->buffer_size()) {
1898 change_bufsize = true;
1901 if (get_midi_option() != backend->midi_option()) {
1905 /* zero-requested channels means "all available" */
1908 ichan = backend->input_channels();
1912 ochan = backend->output_channels();
1915 if (ichan != backend->input_channels()) {
1916 change_channels = true;
1919 if (ochan != backend->output_channels()) {
1920 change_channels = true;
1923 if (get_input_latency() != backend->systemic_input_latency() ||
1924 get_output_latency() != backend->systemic_output_latency()) {
1925 change_latency = true;
1928 /* backend never started, so we have to force a group
1931 change_device = true;
1932 if (backend->requires_driver_selection()) {
1933 change_driver = true;
1936 change_bufsize = true;
1937 change_channels = true;
1938 change_latency = true;
1944 /* we have no control over the backend, meaning that we can
1945 * only possibly change sample rate and buffer size.
1949 if (get_rate() != backend->sample_rate()) {
1950 change_bufsize = true;
1953 if (get_buffer_size() != backend->buffer_size()) {
1954 change_bufsize = true;
1958 queue_device_changed = false;
1960 if (!_have_control) {
1962 /* We do not have control over the backend, so the best we can
1963 * do is try to change the sample rate and/or bufsize and get
1967 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1971 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1976 backend->set_sample_rate (get_rate());
1979 if (change_bufsize) {
1980 backend->set_buffer_size (get_buffer_size());
1984 if (ARDOUR::AudioEngine::instance()->start ()) {
1985 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1995 /* determine if we need to stop the backend before changing parameters */
1997 if (change_driver || change_device || change_channels || change_latency ||
1998 (change_rate && !backend->can_change_sample_rate_when_running()) ||
2000 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
2001 restart_required = true;
2003 restart_required = false;
2008 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
2009 /* no changes in any parameters that absolutely require a
2010 * restart, so check those that might be changeable without a
2014 if (change_rate && !backend->can_change_sample_rate_when_running()) {
2015 /* can't do this while running ... */
2016 restart_required = true;
2019 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
2020 /* can't do this while running ... */
2021 restart_required = true;
2027 if (restart_required) {
2028 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
2035 if (change_driver && backend->set_driver (get_driver())) {
2036 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
2039 if (backend->use_separate_input_and_output_devices()) {
2040 if (change_device && backend->set_input_device_name (get_input_device_name())) {
2041 error << string_compose (_("Cannot set input device name to %1"), get_input_device_name()) << endmsg;
2044 if (change_device && backend->set_output_device_name (get_output_device_name())) {
2045 error << string_compose (_("Cannot set output device name to %1"), get_output_device_name()) << endmsg;
2049 if (change_device && backend->set_device_name (get_device_name())) {
2050 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
2054 if (change_rate && backend->set_sample_rate (get_rate())) {
2055 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
2058 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
2059 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
2063 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
2064 if (backend->set_input_channels (get_input_channels())) {
2065 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
2068 if (backend->set_output_channels (get_output_channels())) {
2069 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
2073 if (change_latency) {
2074 if (backend->set_systemic_input_latency (get_input_latency())) {
2075 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
2078 if (backend->set_systemic_output_latency (get_output_latency())) {
2079 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
2085 backend->set_midi_option (get_midi_option());
2089 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
2090 if (_measure_midi) {
2091 if (*p == _measure_midi) {
2092 backend->set_midi_device_enabled ((*p)->name, true);
2094 backend->set_midi_device_enabled ((*p)->name, false);
2098 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
2099 if (backend->can_set_systemic_midi_latencies()) {
2100 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
2101 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
2106 if (start || (was_running && restart_required)) {
2107 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
2118 EngineControl::post_push ()
2120 /* get a pointer to the current state object, creating one if
2124 State state = get_saved_state_for_currently_displayed_backend_and_device ();
2127 state = save_state ();
2135 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
2136 (*i)->active = false;
2139 /* mark this one active (to be used next time the dialog is
2143 state->active = true;
2145 if (_have_control) { // XXX
2146 manage_control_app_sensitivity ();
2149 /* schedule a redisplay of MIDI ports */
2150 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
2155 EngineControl::get_rate () const
2157 float r = atof (sample_rate_combo.get_active_text ());
2158 /* the string may have been translated with an abbreviation for
2159 * thousands, so use a crude heuristic to fix this.
2169 EngineControl::get_buffer_size () const
2171 string txt = buffer_size_combo.get_active_text ();
2174 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
2175 fprintf(stderr, "Find a trout and repeatedly slap the nearest C++ who throws exceptions without catching them.\n");
2176 fprintf(stderr, "Ardour will likely crash now, giving you time to get the trout.\n");
2184 EngineControl::get_midi_option () const
2186 return midi_option_combo.get_active_text();
2190 EngineControl::get_input_channels() const
2192 if (ARDOUR::Profile->get_mixbus()) {
2193 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2194 if (!backend) return 0;
2195 return backend->input_channels();
2197 return (uint32_t) input_channels_adjustment.get_value();
2201 EngineControl::get_output_channels() const
2203 if (ARDOUR::Profile->get_mixbus()) {
2204 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2205 if (!backend) return 0;
2206 return backend->input_channels();
2208 return (uint32_t) output_channels_adjustment.get_value();
2212 EngineControl::get_input_latency() const
2214 return (uint32_t) input_latency_adjustment.get_value();
2218 EngineControl::get_output_latency() const
2220 return (uint32_t) output_latency_adjustment.get_value();
2224 EngineControl::get_backend () const
2226 return backend_combo.get_active_text ();
2230 EngineControl::get_driver () const
2232 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
2233 return driver_combo.get_active_text ();
2240 EngineControl::get_device_name () const
2242 return device_combo.get_active_text ();
2246 EngineControl::get_input_device_name () const
2248 return input_device_combo.get_active_text ();
2252 EngineControl::get_output_device_name () const
2254 return output_device_combo.get_active_text ();
2258 EngineControl::control_app_button_clicked ()
2260 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2266 backend->launch_control_app ();
2270 EngineControl::manage_control_app_sensitivity ()
2272 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2278 string appname = backend->control_app_name();
2280 if (appname.empty()) {
2281 control_app_button.set_sensitive (false);
2283 control_app_button.set_sensitive (true);
2288 EngineControl::set_desired_sample_rate (uint32_t sr)
2290 _desired_sample_rate = sr;
2295 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
2297 if (page_num == 0) {
2298 cancel_button->set_sensitive (true);
2299 ok_button->set_sensitive (true);
2300 apply_button->set_sensitive (true);
2301 _measure_midi.reset();
2303 cancel_button->set_sensitive (false);
2304 ok_button->set_sensitive (false);
2305 apply_button->set_sensitive (false);
2308 if (page_num == midi_tab) {
2310 refresh_midi_display ();
2313 if (page_num == latency_tab) {
2316 if (ARDOUR::AudioEngine::instance()->running()) {
2317 // TODO - mark as 'stopped for latency
2318 ARDOUR_UI::instance()->disconnect_from_engine ();
2322 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
2324 /* save any existing latency values */
2326 uint32_t il = (uint32_t) input_latency.get_value ();
2327 uint32_t ol = (uint32_t) input_latency.get_value ();
2329 /* reset to zero so that our new test instance
2330 will be clean of any existing latency measures.
2332 NB. this should really be done by the backend
2333 when stated for latency measurement.
2336 input_latency.set_value (0);
2337 output_latency.set_value (0);
2339 push_state_to_backend (false);
2343 input_latency.set_value (il);
2344 output_latency.set_value (ol);
2347 // This should be done in push_state_to_backend()
2348 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
2349 disable_latency_tab ();
2352 enable_latency_tab ();
2356 end_latency_detection ();
2357 ARDOUR::AudioEngine::instance()->stop_latency_detection();
2362 /* latency measurement */
2365 EngineControl::check_audio_latency_measurement ()
2367 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2369 if (mtdm->resolve () < 0) {
2370 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2374 if (mtdm->err () > 0.3) {
2380 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2382 if (sample_rate == 0) {
2383 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2384 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2388 int frames_total = mtdm->del();
2389 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2391 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
2392 _("Detected roundtrip latency: "),
2393 frames_total, frames_total * 1000.0f/sample_rate,
2394 _("Systemic latency: "),
2395 extra, extra * 1000.0f/sample_rate);
2399 if (mtdm->err () > 0.2) {
2401 strcat (buf, _("(signal detection error)"));
2407 strcat (buf, _("(inverted - bad wiring)"));
2411 lm_results.set_markup (string_compose (results_markup, buf));
2414 have_lm_results = true;
2415 end_latency_detection ();
2416 lm_use_button.set_sensitive (true);
2424 EngineControl::check_midi_latency_measurement ()
2426 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2428 if (!mididm->have_signal () || mididm->latency () == 0) {
2429 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
2434 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
2436 if (sample_rate == 0) {
2437 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
2438 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2442 ARDOUR::framecnt_t frames_total = mididm->latency();
2443 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2444 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
2445 _("Detected roundtrip latency: "),
2446 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
2447 _("Systemic latency: "),
2448 extra, extra * 1000.0f / sample_rate);
2452 if (!mididm->ok ()) {
2454 strcat (buf, _("(averaging)"));
2458 if (mididm->deviation () > 50.0) {
2460 strcat (buf, _("(too large jitter)"));
2462 } else if (mididm->deviation () > 10.0) {
2464 strcat (buf, _("(large jitter)"));
2468 have_lm_results = true;
2469 end_latency_detection ();
2470 lm_use_button.set_sensitive (true);
2471 lm_results.set_markup (string_compose (results_markup, buf));
2473 } else if (mididm->processed () > 400) {
2474 have_lm_results = false;
2475 end_latency_detection ();
2476 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
2480 lm_results.set_markup (string_compose (results_markup, buf));
2486 EngineControl::start_latency_detection ()
2488 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
2489 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
2491 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
2492 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
2493 if (_measure_midi) {
2494 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
2496 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
2498 lm_measure_label.set_text (_("Cancel"));
2499 have_lm_results = false;
2500 lm_use_button.set_sensitive (false);
2501 lm_input_channel_combo.set_sensitive (false);
2502 lm_output_channel_combo.set_sensitive (false);
2508 EngineControl::end_latency_detection ()
2510 latency_timeout.disconnect ();
2511 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
2512 lm_measure_label.set_text (_("Measure"));
2513 if (!have_lm_results) {
2514 lm_use_button.set_sensitive (false);
2516 lm_input_channel_combo.set_sensitive (true);
2517 lm_output_channel_combo.set_sensitive (true);
2522 EngineControl::latency_button_clicked ()
2525 start_latency_detection ();
2527 end_latency_detection ();
2532 EngineControl::use_latency_button_clicked ()
2534 if (_measure_midi) {
2535 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2539 ARDOUR::framecnt_t frames_total = mididm->latency();
2540 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2541 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2542 _measure_midi->input_latency = one_way;
2543 _measure_midi->output_latency = one_way;
2544 notebook.set_current_page (midi_tab);
2546 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2552 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2553 one_way = std::max (0., one_way);
2555 input_latency_adjustment.set_value (one_way);
2556 output_latency_adjustment.set_value (one_way);
2558 /* back to settings page */
2559 notebook.set_current_page (0);
2565 EngineControl::on_delete_event (GdkEventAny* ev)
2567 if (notebook.get_current_page() == 2) {
2568 /* currently on latency tab - be sure to clean up */
2569 end_latency_detection ();
2571 return ArdourDialog::on_delete_event (ev);
2575 EngineControl::engine_running ()
2577 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2580 set_active_text_if_present (buffer_size_combo, bufsize_as_string (backend->buffer_size()));
2581 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2583 buffer_size_combo.set_sensitive (true);
2584 sample_rate_combo.set_sensitive (true);
2586 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2587 connect_disconnect_button.show();
2589 started_at_least_once = true;
2590 engine_status.set_markup(string_compose ("<span foreground=\"green\">%1</span>", _("Active")));
2594 EngineControl::engine_stopped ()
2596 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2599 buffer_size_combo.set_sensitive (false);
2600 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2601 connect_disconnect_button.show();
2603 sample_rate_combo.set_sensitive (true);
2604 buffer_size_combo.set_sensitive (true);
2605 engine_status.set_markup(string_compose ("<span foreground=\"red\">%1</span>", _("Inactive")));
2609 EngineControl::device_list_changed ()
2611 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1); // ??
2613 midi_option_changed();
2617 EngineControl::connect_disconnect_click()
2619 if (ARDOUR::AudioEngine::instance()->running()) {
2620 ARDOUR_UI::instance()->disconnect_from_engine ();
2622 ARDOUR_UI::instance()->reconnect_to_engine ();
2627 EngineControl::calibrate_audio_latency ()
2629 _measure_midi.reset ();
2630 have_lm_results = false;
2631 lm_use_button.set_sensitive (false);
2632 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2633 notebook.set_current_page (latency_tab);
2637 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2640 have_lm_results = false;
2641 lm_use_button.set_sensitive (false);
2642 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2643 notebook.set_current_page (latency_tab);
2647 EngineControl::configure_midi_devices ()
2649 notebook.set_current_page (midi_tab);