2 Copyright (C) 2010 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <boost/scoped_ptr.hpp>
28 #include <gtkmm/messagedialog.h>
30 #include "pbd/error.h"
31 #include "pbd/xml++.h"
32 #include "pbd/unwind.h"
33 #include "pbd/failed_constructor.h"
35 #include <gtkmm/alignment.h>
36 #include <gtkmm/stock.h>
37 #include <gtkmm/notebook.h>
38 #include <gtkmm2ext/utils.h>
40 #include "ardour/audio_backend.h"
41 #include "ardour/audioengine.h"
42 #include "ardour/mtdm.h"
43 #include "ardour/mididm.h"
44 #include "ardour/rc_configuration.h"
45 #include "ardour/types.h"
47 #include "pbd/convert.h"
48 #include "pbd/error.h"
50 #include "ardour_ui.h"
51 #include "engine_dialog.h"
52 #include "gui_thread.h"
58 using namespace Gtkmm2ext;
61 using namespace ARDOUR_UI_UTILS;
63 static const unsigned int midi_tab = 2;
64 static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
66 static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
68 EngineControl::EngineControl ()
69 : ArdourDialog (_("Audio/MIDI Setup"))
71 , input_latency_adjustment (0, 0, 99999, 1)
72 , input_latency (input_latency_adjustment)
73 , output_latency_adjustment (0, 0, 99999, 1)
74 , output_latency (output_latency_adjustment)
75 , input_channels_adjustment (0, 0, 256, 1)
76 , input_channels (input_channels_adjustment)
77 , output_channels_adjustment (0, 0, 256, 1)
78 , output_channels (output_channels_adjustment)
79 , ports_adjustment (128, 8, 1024, 1, 16)
80 , ports_spinner (ports_adjustment)
81 , control_app_button (_("Device Control Panel"))
82 , midi_devices_button (_("Midi Device Setup"))
83 , lm_measure_label (_("Measure"))
84 , lm_use_button (_("Use results"))
85 , lm_back_button (_("Back to settings ... (ignore results)"))
86 , lm_button_audio (_("Calibrate Audio"))
88 , have_lm_results (false)
90 , midi_back_button (_("Back to settings"))
92 , _desired_sample_rate (0)
93 , started_at_least_once (false)
95 using namespace Notebook_Helpers;
96 vector<string> strings;
98 AttachOptions xopt = AttachOptions (FILL|EXPAND);
101 set_name (X_("AudioMIDISetup"));
103 /* the backend combo is the one thing that is ALWAYS visible */
105 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
107 if (backends.empty()) {
108 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));
110 throw failed_constructor ();
113 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
114 strings.push_back ((*b)->name);
117 set_popdown_strings (backend_combo, strings);
118 backend_combo.set_active_text (strings.front());
119 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
121 /* setup basic packing characteristics for the table used on the main
122 * tab of the notebook
125 basic_packer.set_spacings (6);
126 basic_packer.set_border_width (12);
127 basic_packer.set_homogeneous (false);
131 basic_hbox.pack_start (basic_packer, false, false);
133 /* latency measurement tab */
135 lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
138 lm_table.set_row_spacings (12);
139 lm_table.set_col_spacings (6);
140 lm_table.set_homogeneous (false);
142 lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
145 lm_preamble.set_width_chars (60);
146 lm_preamble.set_line_wrap (true);
147 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
149 lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
152 Gtk::Label* preamble;
153 preamble = manage (new Label);
154 preamble->set_width_chars (60);
155 preamble->set_line_wrap (true);
156 preamble->set_markup (_("Select two channels below and connect them using a cable."));
158 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
161 label = manage (new Label (_("Output channel")));
162 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
164 Gtk::Alignment* misc_align = manage (new Alignment (0.0, 0.5));
165 misc_align->add (lm_output_channel_combo);
166 lm_table.attach (*misc_align, 1, 3, row, row+1, xopt, (AttachOptions) 0);
169 label = manage (new Label (_("Input channel")));
170 lm_table.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
172 misc_align = manage (new Alignment (0.0, 0.5));
173 misc_align->add (lm_input_channel_combo);
174 lm_table.attach (*misc_align, 1, 3, row, row+1, FILL, (AttachOptions) 0);
177 xopt = AttachOptions(0);
179 lm_measure_label.set_padding (10, 10);
180 lm_measure_button.add (lm_measure_label);
181 lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
182 lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
183 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
185 lm_use_button.set_sensitive (false);
187 /* Increase the default spacing around the labels of these three
193 if ((l = dynamic_cast<Gtk::Misc*>(lm_use_button.get_child())) != 0) {
194 l->set_padding (10, 10);
197 if ((l = dynamic_cast<Gtk::Misc*>(lm_back_button.get_child())) != 0) {
198 l->set_padding (10, 10);
201 preamble = manage (new Label);
202 preamble->set_width_chars (60);
203 preamble->set_line_wrap (true);
204 preamble->set_markup (_("Once the channels are connected, click the \"Measure\" button."));
205 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
208 preamble = manage (new Label);
209 preamble->set_width_chars (60);
210 preamble->set_line_wrap (true);
211 preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
212 lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
214 ++row; // skip a row in the table
215 ++row; // skip a row in the table
217 lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
219 ++row; // skip a row in the table
220 ++row; // skip a row in the table
222 lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
223 lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
224 lm_table.attach (lm_back_button, 2, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
226 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
228 lm_vbox.set_border_width (12);
229 lm_vbox.pack_start (lm_table, false, false);
231 midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
235 notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
236 notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
237 notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
238 notebook.set_border_width (12);
240 notebook.set_show_tabs (false);
241 notebook.show_all ();
243 notebook.set_name ("SettingsNotebook");
245 /* packup the notebook */
247 get_vbox()->set_border_width (12);
248 get_vbox()->pack_start (notebook);
250 /* need a special function to print "all available channels" when the
251 * channel counts hit zero.
254 input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
255 output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
257 midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
258 midi_devices_button.set_sensitive (false);
259 midi_devices_button.set_name ("generic button");
260 midi_devices_button.set_can_focus(true);
262 control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
263 manage_control_app_sensitivity ();
265 cancel_button = add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
266 ok_button = add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
267 apply_button = add_button (Gtk::Stock::APPLY, Gtk::RESPONSE_APPLY);
269 /* Pick up any existing audio setup configuration, if appropriate */
271 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioMIDISetup");
273 ARDOUR::AudioEngine::instance()->Running.connect (running_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_running, this), gui_context());
274 ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
275 ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
279 set_state (*audio_setup);
282 /* ignore: don't save state */
283 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
287 /* Connect to signals */
289 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
290 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::sample_rate_changed));
291 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::buffer_size_changed));
292 device_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::device_changed));
293 midi_option_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::midi_option_changed));
295 input_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
296 output_latency.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
297 input_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
298 output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
300 notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
304 EngineControl::on_response (int response_id)
306 ArdourDialog::on_response (response_id);
308 switch (response_id) {
310 push_state_to_backend (true);
313 push_state_to_backend (true);
316 case RESPONSE_DELETE_EVENT:
319 ev.type = GDK_BUTTON_PRESS;
321 on_delete_event ((GdkEventAny*) &ev);
330 EngineControl::build_notebook ()
333 AttachOptions xopt = AttachOptions (FILL|EXPAND);
335 /* clear the table */
337 Gtkmm2ext::container_clear (basic_vbox);
338 Gtkmm2ext::container_clear (basic_packer);
340 if (control_app_button.get_parent()) {
341 control_app_button.get_parent()->remove (control_app_button);
344 label = manage (left_aligned_label (_("Audio System:")));
345 basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
346 basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
348 lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
349 lm_button_audio.set_name ("generic button");
350 lm_button_audio.set_can_focus(true);
353 build_full_control_notebook ();
355 build_no_control_notebook ();
358 basic_vbox.pack_start (basic_hbox, false, false);
361 Gtk::HBox* hpacker = manage (new HBox);
362 hpacker->set_border_width (12);
363 hpacker->pack_start (control_app_button, false, false);
365 control_app_button.show();
366 basic_vbox.pack_start (*hpacker);
369 basic_vbox.show_all ();
373 EngineControl::build_full_control_notebook ()
375 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
378 using namespace Notebook_Helpers;
380 vector<string> strings;
381 AttachOptions xopt = AttachOptions (FILL|EXPAND);
382 int row = 1; // row zero == backend combo
384 /* start packing it up */
386 if (backend->requires_driver_selection()) {
387 label = manage (left_aligned_label (_("Driver:")));
388 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
389 basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
393 label = manage (left_aligned_label (_("Device:")));
394 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
395 basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
398 label = manage (left_aligned_label (_("Sample rate:")));
399 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
400 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
404 label = manage (left_aligned_label (_("Buffer size:")));
405 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
406 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
407 buffer_size_duration_label.set_alignment (0.0); /* left-align */
408 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
411 input_channels.set_name ("InputChannels");
412 input_channels.set_flags (Gtk::CAN_FOCUS);
413 input_channels.set_digits (0);
414 input_channels.set_wrap (false);
415 output_channels.set_editable (true);
417 label = manage (left_aligned_label (_("Input Channels:")));
418 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
419 basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
422 output_channels.set_name ("OutputChannels");
423 output_channels.set_flags (Gtk::CAN_FOCUS);
424 output_channels.set_digits (0);
425 output_channels.set_wrap (false);
426 output_channels.set_editable (true);
428 label = manage (left_aligned_label (_("Output Channels:")));
429 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
430 basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
433 input_latency.set_name ("InputLatency");
434 input_latency.set_flags (Gtk::CAN_FOCUS);
435 input_latency.set_digits (0);
436 input_latency.set_wrap (false);
437 input_latency.set_editable (true);
439 label = manage (left_aligned_label (_("Hardware input latency:")));
440 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
441 basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
442 label = manage (left_aligned_label (_("samples")));
443 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
446 output_latency.set_name ("OutputLatency");
447 output_latency.set_flags (Gtk::CAN_FOCUS);
448 output_latency.set_digits (0);
449 output_latency.set_wrap (false);
450 output_latency.set_editable (true);
452 label = manage (left_aligned_label (_("Hardware output latency:")));
453 basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
454 basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
455 label = manage (left_aligned_label (_("samples")));
456 basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
458 /* button spans 2 rows */
460 basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
463 label = manage (left_aligned_label (_("MIDI System")));
464 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
465 basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
466 basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
471 EngineControl::build_no_control_notebook ()
473 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
476 using namespace Notebook_Helpers;
478 vector<string> strings;
479 AttachOptions xopt = AttachOptions (FILL|EXPAND);
480 int row = 1; // row zero == backend combo
481 const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
483 label = manage (new Label);
484 label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
485 basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
488 if (backend->can_change_sample_rate_when_running()) {
489 label = manage (left_aligned_label (_("Sample rate:")));
490 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
491 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
495 if (backend->can_change_buffer_size_when_running()) {
496 label = manage (left_aligned_label (_("Buffer size:")));
497 basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
498 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
499 buffer_size_duration_label.set_alignment (0.0); /* left-align */
500 basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
504 connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
506 basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
510 EngineControl::~EngineControl ()
512 ignore_changes = true;
516 EngineControl::disable_latency_tab ()
518 vector<string> empty;
519 set_popdown_strings (lm_output_channel_combo, empty);
520 set_popdown_strings (lm_input_channel_combo, empty);
521 lm_measure_button.set_sensitive (false);
522 lm_use_button.set_sensitive (false);
526 EngineControl::enable_latency_tab ()
528 vector<string> outputs;
529 vector<string> inputs;
531 ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
532 ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
533 ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
535 if (!ARDOUR::AudioEngine::instance()->running()) {
536 MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
537 notebook.set_current_page (0);
541 else if (inputs.empty() || outputs.empty()) {
542 MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
543 notebook.set_current_page (0);
548 lm_back_button_signal.disconnect();
550 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
551 lm_preamble.set_markup (_(""));
553 lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
554 lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
557 set_popdown_strings (lm_output_channel_combo, outputs);
558 lm_output_channel_combo.set_active_text (outputs.front());
559 lm_output_channel_combo.set_sensitive (true);
561 set_popdown_strings (lm_input_channel_combo, inputs);
562 lm_input_channel_combo.set_active_text (inputs.front());
563 lm_input_channel_combo.set_sensitive (true);
565 lm_measure_button.set_sensitive (true);
569 EngineControl::setup_midi_tab_for_backend ()
571 string backend = backend_combo.get_active_text ();
573 Gtkmm2ext::container_clear (midi_vbox);
575 midi_vbox.set_border_width (12);
576 midi_device_table.set_border_width (12);
578 if (backend == "JACK") {
579 setup_midi_tab_for_jack ();
582 midi_vbox.pack_start (midi_device_table, true, true);
583 midi_vbox.pack_start (midi_back_button, false, false);
584 midi_vbox.show_all ();
588 EngineControl::setup_midi_tab_for_jack ()
593 EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
595 device->input_latency = a->get_value();
597 device->output_latency = a->get_value();
602 EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
603 b->set_active (!b->get_active());
604 device->enabled = b->get_active();
605 refresh_midi_display(device->name);
609 EngineControl::refresh_midi_display (std::string focus)
611 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
615 AttachOptions xopt = AttachOptions (FILL|EXPAND);
618 Gtkmm2ext::container_clear (midi_device_table);
620 midi_device_table.set_spacings (6);
622 l = manage (new Label);
623 l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
624 midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
625 l->set_alignment (0.5, 0.5);
629 l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
630 midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
631 l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
632 midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
634 l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
635 midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
636 l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
637 midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
640 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
645 bool enabled = (*p)->enabled;
647 m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
648 m->set_name ("midi device");
649 m->set_can_focus (Gtk::CAN_FOCUS);
650 m->add_events (Gdk::BUTTON_RELEASE_MASK);
651 m->set_active (enabled);
652 m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
653 midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
654 if ((*p)->name == focus) {
658 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
659 s = manage (new Gtk::SpinButton (*a));
660 a->set_value ((*p)->input_latency);
661 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
662 s->set_sensitive (_can_set_midi_latencies && enabled);
663 midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
665 a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
666 s = manage (new Gtk::SpinButton (*a));
667 a->set_value ((*p)->output_latency);
668 s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
669 s->set_sensitive (_can_set_midi_latencies && enabled);
670 midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
672 b = manage (new Button (_("Calibrate")));
673 b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
674 b->set_sensitive (_can_set_midi_latencies && enabled);
675 midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
682 EngineControl::update_sensitivity ()
687 EngineControl::backend_changed ()
689 string backend_name = backend_combo.get_active_text();
690 boost::shared_ptr<ARDOUR::AudioBackend> backend;
692 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
693 /* eh? setting the backend failed... how ? */
697 _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
700 setup_midi_tab_for_backend ();
701 _midi_devices.clear();
703 if (backend->requires_driver_selection()) {
704 vector<string> drivers = backend->enumerate_drivers();
705 driver_combo.set_sensitive (true);
707 if (!drivers.empty()) {
709 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
710 set_popdown_strings (driver_combo, drivers);
711 driver_combo.set_active_text (drivers.front());
718 driver_combo.set_sensitive (false);
719 /* this will change the device text which will cause a call to
720 * device changed which will set up parameters
725 vector<string> midi_options = backend->enumerate_midi_options();
727 if (midi_options.size() == 1) {
728 /* only contains the "none" option */
729 midi_option_combo.set_sensitive (false);
732 set_popdown_strings (midi_option_combo, midi_options);
733 midi_option_combo.set_active_text (midi_options.front());
734 midi_option_combo.set_sensitive (true);
736 midi_option_combo.set_sensitive (false);
740 midi_option_changed();
742 started_at_least_once = false;
744 if (!ignore_changes) {
745 maybe_display_saved_state ();
750 EngineControl::print_channel_count (Gtk::SpinButton* sb)
752 uint32_t cnt = (uint32_t) sb->get_value();
754 sb->set_text (_("all available channels"));
757 snprintf (buf, sizeof (buf), "%d", cnt);
764 EngineControl::list_devices ()
766 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
769 /* now fill out devices, mark sample rates, buffer sizes insensitive */
771 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
773 /* NOTE: Ardour currently does not display the "available" field of the
776 * Doing so would require a different GUI widget than the combo
777 * box/popdown that we currently use, since it has no way to list
778 * items that are not selectable. Something more like a popup menu,
779 * which could have unselectable items, would be appropriate.
782 vector<string> available_devices;
784 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
785 available_devices.push_back (i->name);
788 if (!available_devices.empty()) {
790 update_sensitivity ();
793 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
794 set_popdown_strings (device_combo, available_devices);
795 device_combo.set_active_text (available_devices.front());
800 ok_button->set_sensitive (true);
801 apply_button->set_sensitive (true);
804 device_combo.clear();
805 sample_rate_combo.set_sensitive (false);
806 buffer_size_combo.set_sensitive (false);
807 input_latency.set_sensitive (false);
808 output_latency.set_sensitive (false);
809 input_channels.set_sensitive (false);
810 output_channels.set_sensitive (false);
812 ok_button->set_sensitive (false);
813 apply_button->set_sensitive (false);
815 ok_button->set_sensitive (true);
816 apply_button->set_sensitive (true);
822 EngineControl::driver_changed ()
824 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
827 backend->set_driver (driver_combo.get_active_text());
830 if (!ignore_changes) {
831 maybe_display_saved_state ();
836 EngineControl::device_changed ()
839 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
841 string device_name = device_combo.get_active_text ();
845 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
847 /* don't allow programmatic change to combos to cause a
848 recursive call to this method.
858 sr = backend->available_sample_rates (device_name);
861 sr.push_back (8000.0f);
862 sr.push_back (16000.0f);
863 sr.push_back (32000.0f);
864 sr.push_back (44100.0f);
865 sr.push_back (48000.0f);
866 sr.push_back (88200.0f);
867 sr.push_back (96000.0f);
868 sr.push_back (192000.0f);
869 sr.push_back (384000.0f);
872 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
873 s.push_back (rate_as_string (*x));
874 if (*x == _desired_sample_rate) {
880 sample_rate_combo.set_sensitive (true);
881 set_popdown_strings (sample_rate_combo, s);
883 if (desired.empty()) {
884 sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
886 sample_rate_combo.set_active_text (desired);
890 sample_rate_combo.set_sensitive (false);
898 bs = backend->available_buffer_sizes (device_name);
899 } else if (backend->can_change_buffer_size_when_running()) {
913 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
914 s.push_back (bufsize_as_string (*x));
918 buffer_size_combo.set_sensitive (true);
919 set_popdown_strings (buffer_size_combo, s);
921 buffer_size_combo.set_active_text (bufsize_as_string (backend->default_buffer_size()));
922 show_buffer_duration ();
924 buffer_size_combo.set_sensitive (false);
927 /* XXX theoretically need to set min + max channel counts here
930 manage_control_app_sensitivity ();
933 /* pick up any saved state for this device */
935 if (!ignore_changes) {
936 maybe_display_saved_state ();
941 EngineControl::bufsize_as_string (uint32_t sz)
943 /* Translators: "samples" is always plural here, so no
944 need for plural+singular forms.
947 snprintf (buf, sizeof (buf), _("%u samples"), sz);
952 EngineControl::sample_rate_changed ()
954 /* reset the strings for buffer size to show the correct msec value
955 (reflecting the new sample rate).
958 show_buffer_duration ();
959 if (!ignore_changes) {
966 EngineControl::buffer_size_changed ()
968 show_buffer_duration ();
969 if (!ignore_changes) {
975 EngineControl::show_buffer_duration ()
978 /* buffer sizes - convert from just samples to samples + msecs for
979 * the displayed string
982 string bs_text = buffer_size_combo.get_active_text ();
983 uint32_t samples = atoi (bs_text); /* will ignore trailing text */
984 uint32_t rate = get_rate();
986 /* Translators: "msecs" is ALWAYS plural here, so we do not
987 need singular form as well.
989 /* Developers: note the hard-coding of a double buffered model
990 in the (2 * samples) computation of latency. we always start
991 the audiobackend in this configuration.
994 snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
995 buffer_size_duration_label.set_text (buf);
999 EngineControl::midi_option_changed ()
1001 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1004 backend->set_midi_option (get_midi_option());
1006 vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
1008 //_midi_devices.clear(); // TODO merge with state-saved settings..
1009 _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
1010 std::vector<MidiDeviceSettings> new_devices;
1012 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
1013 MidiDeviceSettings mds = find_midi_device (i->name);
1014 if (i->available && !mds) {
1015 uint32_t input_latency = 0;
1016 uint32_t output_latency = 0;
1017 if (_can_set_midi_latencies) {
1018 input_latency = backend->systemic_midi_input_latency (i->name);
1019 output_latency = backend->systemic_midi_output_latency (i->name);
1021 bool enabled = backend->midi_device_enabled (i->name);
1022 MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
1023 new_devices.push_back (ptr);
1024 } else if (i->available) {
1025 new_devices.push_back (mds);
1028 _midi_devices = new_devices;
1030 if (_midi_devices.empty()) {
1031 midi_devices_button.set_sensitive (false);
1033 midi_devices_button.set_sensitive (true);
1036 if (!ignore_changes) {
1042 EngineControl::parameter_changed ()
1044 if (!ignore_changes) {
1049 EngineControl::State
1050 EngineControl::get_matching_state (
1051 const string& backend,
1052 const string& driver,
1053 const string& device)
1055 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1056 if ((*i)->backend == backend &&
1057 (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
1065 EngineControl::State
1066 EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
1068 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1071 return get_matching_state (backend_combo.get_active_text(),
1072 (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
1073 device_combo.get_active_text());
1077 return get_matching_state (backend_combo.get_active_text(),
1079 device_combo.get_active_text());
1082 EngineControl::State
1083 EngineControl::save_state ()
1087 if (!_have_control) {
1088 state = get_matching_state (backend_combo.get_active_text(), string(), string());
1092 state.reset(new StateStruct);
1093 state->backend = get_backend ();
1095 state.reset(new StateStruct);
1096 store_state (state);
1099 for (StateList::iterator i = states.begin(); i != states.end();) {
1100 if ((*i)->backend == state->backend &&
1101 (*i)->driver == state->driver &&
1102 (*i)->device == state->device) {
1103 i = states.erase(i);
1109 states.push_back (state);
1115 EngineControl::store_state (State state)
1117 state->backend = get_backend ();
1118 state->driver = get_driver ();
1119 state->device = get_device_name ();
1120 state->sample_rate = get_rate ();
1121 state->buffer_size = get_buffer_size ();
1122 state->input_latency = get_input_latency ();
1123 state->output_latency = get_output_latency ();
1124 state->input_channels = get_input_channels ();
1125 state->output_channels = get_output_channels ();
1126 state->midi_option = get_midi_option ();
1127 state->midi_devices = _midi_devices;
1131 EngineControl::maybe_display_saved_state ()
1133 if (!_have_control) {
1137 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1140 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1142 if (!_desired_sample_rate) {
1143 sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
1145 buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
1146 /* call this explicitly because we're ignoring changes to
1147 the controls at this point.
1149 show_buffer_duration ();
1150 input_latency.set_value (state->input_latency);
1151 output_latency.set_value (state->output_latency);
1153 if (!state->midi_option.empty()) {
1154 midi_option_combo.set_active_text (state->midi_option);
1155 _midi_devices = state->midi_devices;
1161 EngineControl::get_state ()
1163 XMLNode* root = new XMLNode ("AudioMIDISetup");
1166 if (!states.empty()) {
1167 XMLNode* state_nodes = new XMLNode ("EngineStates");
1169 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1171 XMLNode* node = new XMLNode ("State");
1173 node->add_property ("backend", (*i)->backend);
1174 node->add_property ("driver", (*i)->driver);
1175 node->add_property ("device", (*i)->device);
1176 node->add_property ("sample-rate", (*i)->sample_rate);
1177 node->add_property ("buffer-size", (*i)->buffer_size);
1178 node->add_property ("input-latency", (*i)->input_latency);
1179 node->add_property ("output-latency", (*i)->output_latency);
1180 node->add_property ("input-channels", (*i)->input_channels);
1181 node->add_property ("output-channels", (*i)->output_channels);
1182 node->add_property ("active", (*i)->active ? "yes" : "no");
1183 node->add_property ("midi-option", (*i)->midi_option);
1185 XMLNode* midi_devices = new XMLNode ("MIDIDevices");
1186 for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
1187 XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
1188 midi_device_stuff->add_property (X_("name"), (*p)->name);
1189 midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
1190 midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
1191 midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
1192 midi_devices->add_child_nocopy (*midi_device_stuff);
1194 node->add_child_nocopy (*midi_devices);
1196 state_nodes->add_child_nocopy (*node);
1199 root->add_child_nocopy (*state_nodes);
1206 EngineControl::set_state (const XMLNode& root)
1208 XMLNodeList clist, cclist;
1209 XMLNodeConstIterator citer, cciter;
1211 XMLNode* grandchild;
1212 XMLProperty* prop = NULL;
1214 if (root.name() != "AudioMIDISetup") {
1218 clist = root.children();
1222 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1226 if (child->name() != "EngineStates") {
1230 cclist = child->children();
1232 for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
1233 State state (new StateStruct);
1235 grandchild = *cciter;
1237 if (grandchild->name() != "State") {
1241 if ((prop = grandchild->property ("backend")) == 0) {
1244 state->backend = prop->value ();
1246 if ((prop = grandchild->property ("driver")) == 0) {
1249 state->driver = prop->value ();
1251 if ((prop = grandchild->property ("device")) == 0) {
1254 state->device = prop->value ();
1256 if ((prop = grandchild->property ("sample-rate")) == 0) {
1259 state->sample_rate = atof (prop->value ());
1261 if ((prop = grandchild->property ("buffer-size")) == 0) {
1264 state->buffer_size = atoi (prop->value ());
1266 if ((prop = grandchild->property ("input-latency")) == 0) {
1269 state->input_latency = atoi (prop->value ());
1271 if ((prop = grandchild->property ("output-latency")) == 0) {
1274 state->output_latency = atoi (prop->value ());
1276 if ((prop = grandchild->property ("input-channels")) == 0) {
1279 state->input_channels = atoi (prop->value ());
1281 if ((prop = grandchild->property ("output-channels")) == 0) {
1284 state->output_channels = atoi (prop->value ());
1286 if ((prop = grandchild->property ("active")) == 0) {
1289 state->active = string_is_affirmative (prop->value ());
1291 if ((prop = grandchild->property ("midi-option")) == 0) {
1294 state->midi_option = prop->value ();
1296 state->midi_devices.clear();
1298 if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
1299 const XMLNodeList mnc = midinode->children();
1300 for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
1301 if ((*n)->property (X_("name")) == 0
1302 || (*n)->property (X_("enabled")) == 0
1303 || (*n)->property (X_("input-latency")) == 0
1304 || (*n)->property (X_("output-latency")) == 0
1309 MidiDeviceSettings ptr (new MidiDeviceSetting(
1310 (*n)->property (X_("name"))->value (),
1311 string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
1312 atoi ((*n)->property (X_("input-latency"))->value ()),
1313 atoi ((*n)->property (X_("output-latency"))->value ())
1315 state->midi_devices.push_back (ptr);
1320 /* remove accumulated duplicates (due to bug in ealier version)
1321 * this can be removed again before release
1323 for (StateList::iterator i = states.begin(); i != states.end();) {
1324 if ((*i)->backend == state->backend &&
1325 (*i)->driver == state->driver &&
1326 (*i)->device == state->device) {
1327 i = states.erase(i);
1334 states.push_back (state);
1338 /* now see if there was an active state and switch the setup to it */
1340 // purge states of backend that are not available in this built
1341 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
1342 vector<std::string> backend_names;
1344 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator i = backends.begin(); i != backends.end(); ++i) {
1345 backend_names.push_back((*i)->name);
1347 for (StateList::iterator i = states.begin(); i != states.end();) {
1348 if (std::find(backend_names.begin(), backend_names.end(), (*i)->backend) == backend_names.end()) {
1349 i = states.erase(i);
1355 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1359 backend_combo.set_active_text ((*i)->backend);
1360 driver_combo.set_active_text ((*i)->driver);
1361 device_combo.set_active_text ((*i)->device);
1362 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1363 buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
1364 input_latency.set_value ((*i)->input_latency);
1365 output_latency.set_value ((*i)->output_latency);
1366 midi_option_combo.set_active_text ((*i)->midi_option);
1374 EngineControl::push_state_to_backend (bool start)
1376 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1382 /* figure out what is going to change */
1384 bool restart_required = false;
1385 bool was_running = ARDOUR::AudioEngine::instance()->running();
1386 bool change_driver = false;
1387 bool change_device = false;
1388 bool change_rate = false;
1389 bool change_bufsize = false;
1390 bool change_latency = false;
1391 bool change_channels = false;
1392 bool change_midi = false;
1394 uint32_t ochan = get_output_channels ();
1395 uint32_t ichan = get_input_channels ();
1397 if (_have_control) {
1399 if (started_at_least_once) {
1401 /* we can control the backend */
1403 if (backend->requires_driver_selection()) {
1404 if (get_driver() != backend->driver_name()) {
1405 change_driver = true;
1409 if (get_device_name() != backend->device_name()) {
1410 change_device = true;
1413 if (get_rate() != backend->sample_rate()) {
1417 if (get_buffer_size() != backend->buffer_size()) {
1418 change_bufsize = true;
1421 if (get_midi_option() != backend->midi_option()) {
1425 /* zero-requested channels means "all available" */
1428 ichan = backend->input_channels();
1432 ochan = backend->output_channels();
1435 if (ichan != backend->input_channels()) {
1436 change_channels = true;
1439 if (ochan != backend->output_channels()) {
1440 change_channels = true;
1443 if (get_input_latency() != backend->systemic_input_latency() ||
1444 get_output_latency() != backend->systemic_output_latency()) {
1445 change_latency = true;
1448 /* backend never started, so we have to force a group
1451 change_device = true;
1452 if (backend->requires_driver_selection()) {
1453 change_driver = true;
1456 change_bufsize = true;
1457 change_channels = true;
1458 change_latency = true;
1464 /* we have no control over the backend, meaning that we can
1465 * only possibly change sample rate and buffer size.
1469 if (get_rate() != backend->sample_rate()) {
1470 change_bufsize = true;
1473 if (get_buffer_size() != backend->buffer_size()) {
1474 change_bufsize = true;
1478 if (!_have_control) {
1480 /* We do not have control over the backend, so the best we can
1481 * do is try to change the sample rate and/or bufsize and get
1485 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1489 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1494 backend->set_sample_rate (get_rate());
1497 if (change_bufsize) {
1498 backend->set_buffer_size (get_buffer_size());
1502 if (ARDOUR::AudioEngine::instance()->start ()) {
1503 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1513 /* determine if we need to stop the backend before changing parameters */
1515 if (change_driver || change_device || change_channels || change_latency ||
1516 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1518 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1519 restart_required = true;
1521 restart_required = false;
1526 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1527 /* no changes in any parameters that absolutely require a
1528 * restart, so check those that might be changeable without a
1532 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1533 /* can't do this while running ... */
1534 restart_required = true;
1537 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1538 /* can't do this while running ... */
1539 restart_required = true;
1545 if (restart_required) {
1546 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1553 if (change_driver && backend->set_driver (get_driver())) {
1554 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1557 if (change_device && backend->set_device_name (get_device_name())) {
1558 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1561 if (change_rate && backend->set_sample_rate (get_rate())) {
1562 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1565 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1566 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1570 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1571 if (backend->set_input_channels (get_input_channels())) {
1572 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1575 if (backend->set_output_channels (get_output_channels())) {
1576 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1580 if (change_latency) {
1581 if (backend->set_systemic_input_latency (get_input_latency())) {
1582 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1585 if (backend->set_systemic_output_latency (get_output_latency())) {
1586 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1592 backend->set_midi_option (get_midi_option());
1596 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1597 if (_measure_midi) {
1598 if (*p == _measure_midi) {
1599 backend->set_midi_device_enabled ((*p)->name, true);
1601 backend->set_midi_device_enabled ((*p)->name, false);
1605 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1606 if (backend->can_set_systemic_midi_latencies()) {
1607 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1608 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1613 if (start || (was_running && restart_required)) {
1614 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1625 EngineControl::post_push ()
1627 /* get a pointer to the current state object, creating one if
1631 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1634 state = save_state ();
1640 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1641 (*i)->active = false;
1644 /* mark this one active (to be used next time the dialog is
1648 state->active = true;
1650 if (_have_control) { // XXX
1651 manage_control_app_sensitivity ();
1654 /* schedule a redisplay of MIDI ports */
1655 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1660 EngineControl::get_rate () const
1662 float r = atof (sample_rate_combo.get_active_text ());
1663 /* the string may have been translated with an abbreviation for
1664 * thousands, so use a crude heuristic to fix this.
1674 EngineControl::get_buffer_size () const
1676 string txt = buffer_size_combo.get_active_text ();
1679 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1687 EngineControl::get_midi_option () const
1689 return midi_option_combo.get_active_text();
1693 EngineControl::get_input_channels() const
1695 return (uint32_t) input_channels_adjustment.get_value();
1699 EngineControl::get_output_channels() const
1701 return (uint32_t) output_channels_adjustment.get_value();
1705 EngineControl::get_input_latency() const
1707 return (uint32_t) input_latency_adjustment.get_value();
1711 EngineControl::get_output_latency() const
1713 return (uint32_t) output_latency_adjustment.get_value();
1717 EngineControl::get_backend () const
1719 return backend_combo.get_active_text ();
1723 EngineControl::get_driver () const
1725 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1726 return driver_combo.get_active_text ();
1733 EngineControl::get_device_name () const
1735 return device_combo.get_active_text ();
1739 EngineControl::control_app_button_clicked ()
1741 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1747 backend->launch_control_app ();
1751 EngineControl::manage_control_app_sensitivity ()
1753 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1759 string appname = backend->control_app_name();
1761 if (appname.empty()) {
1762 control_app_button.set_sensitive (false);
1764 control_app_button.set_sensitive (true);
1769 EngineControl::set_desired_sample_rate (uint32_t sr)
1771 _desired_sample_rate = sr;
1776 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1778 if (page_num == 0) {
1779 cancel_button->set_sensitive (true);
1780 ok_button->set_sensitive (true);
1781 apply_button->set_sensitive (true);
1782 _measure_midi.reset();
1784 cancel_button->set_sensitive (false);
1785 ok_button->set_sensitive (false);
1786 apply_button->set_sensitive (false);
1789 if (page_num == midi_tab) {
1791 refresh_midi_display ();
1794 if (page_num == latency_tab) {
1797 if (ARDOUR::AudioEngine::instance()->running()) {
1798 // TODO - mark as 'stopped for latency
1799 ARDOUR_UI::instance()->disconnect_from_engine ();
1803 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1805 /* save any existing latency values */
1807 uint32_t il = (uint32_t) input_latency.get_value ();
1808 uint32_t ol = (uint32_t) input_latency.get_value ();
1810 /* reset to zero so that our new test instance
1811 will be clean of any existing latency measures.
1813 NB. this should really be done by the backend
1814 when stated for latency measurement.
1817 input_latency.set_value (0);
1818 output_latency.set_value (0);
1820 push_state_to_backend (false);
1824 input_latency.set_value (il);
1825 output_latency.set_value (ol);
1828 // This should be done in push_state_to_backend()
1829 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1830 disable_latency_tab ();
1833 enable_latency_tab ();
1837 end_latency_detection ();
1838 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1843 /* latency measurement */
1846 EngineControl::check_audio_latency_measurement ()
1848 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1850 if (mtdm->resolve () < 0) {
1851 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1855 if (mtdm->err () > 0.3) {
1861 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1863 if (sample_rate == 0) {
1864 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1865 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1869 int frames_total = mtdm->del();
1870 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1872 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1873 _("Detected roundtrip latency: "),
1874 frames_total, frames_total * 1000.0f/sample_rate,
1875 _("Systemic latency: "),
1876 extra, extra * 1000.0f/sample_rate);
1880 if (mtdm->err () > 0.2) {
1882 strcat (buf, _("(signal detection error)"));
1888 strcat (buf, _("(inverted - bad wiring)"));
1893 have_lm_results = true;
1894 end_latency_detection ();
1895 lm_use_button.set_sensitive (true);
1899 lm_results.set_markup (string_compose (results_markup, buf));
1905 EngineControl::check_midi_latency_measurement ()
1907 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
1909 if (!mididm->have_signal () || mididm->latency () == 0) {
1910 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1915 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1917 if (sample_rate == 0) {
1918 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1919 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1923 ARDOUR::framecnt_t frames_total = mididm->latency();
1924 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1925 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
1926 _("Detected roundtrip latency: "),
1927 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
1928 _("Systemic latency: "),
1929 extra, extra * 1000.0f / sample_rate);
1933 if (!mididm->ok ()) {
1935 strcat (buf, _("(averaging)"));
1939 if (mididm->deviation () > 50.0) {
1941 strcat (buf, _("(too large jitter)"));
1943 } else if (mididm->deviation () > 10.0) {
1945 strcat (buf, _("(large jitter)"));
1949 have_lm_results = true;
1950 end_latency_detection ();
1951 lm_use_button.set_sensitive (true);
1953 } else if (mididm->processed () > 400) {
1954 have_lm_results = false;
1955 end_latency_detection ();
1956 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
1960 lm_results.set_markup (string_compose (results_markup, buf));
1966 EngineControl::start_latency_detection ()
1968 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1969 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1971 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
1972 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
1973 if (_measure_midi) {
1974 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
1976 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
1978 lm_measure_label.set_text (_("Cancel"));
1979 have_lm_results = false;
1980 lm_use_button.set_sensitive (false);
1981 lm_input_channel_combo.set_sensitive (false);
1982 lm_output_channel_combo.set_sensitive (false);
1988 EngineControl::end_latency_detection ()
1990 latency_timeout.disconnect ();
1991 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1992 lm_measure_label.set_text (_("Measure"));
1993 if (!have_lm_results) {
1994 lm_use_button.set_sensitive (false);
1996 lm_input_channel_combo.set_sensitive (true);
1997 lm_output_channel_combo.set_sensitive (true);
2002 EngineControl::latency_button_clicked ()
2005 start_latency_detection ();
2007 end_latency_detection ();
2012 EngineControl::use_latency_button_clicked ()
2014 if (_measure_midi) {
2015 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2019 ARDOUR::framecnt_t frames_total = mididm->latency();
2020 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2021 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2022 _measure_midi->input_latency = one_way;
2023 _measure_midi->output_latency = one_way;
2024 notebook.set_current_page (midi_tab);
2026 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2032 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2033 one_way = std::max (0., one_way);
2035 input_latency_adjustment.set_value (one_way);
2036 output_latency_adjustment.set_value (one_way);
2038 /* back to settings page */
2039 notebook.set_current_page (0);
2045 EngineControl::on_delete_event (GdkEventAny* ev)
2047 if (notebook.get_current_page() == 2) {
2048 /* currently on latency tab - be sure to clean up */
2049 end_latency_detection ();
2051 return ArdourDialog::on_delete_event (ev);
2055 EngineControl::engine_running ()
2057 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2060 buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
2061 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2063 buffer_size_combo.set_sensitive (true);
2064 sample_rate_combo.set_sensitive (true);
2066 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2068 started_at_least_once = true;
2072 EngineControl::engine_stopped ()
2074 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2077 buffer_size_combo.set_sensitive (false);
2078 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2080 sample_rate_combo.set_sensitive (true);
2081 buffer_size_combo.set_sensitive (true);
2085 EngineControl::connect_disconnect_click()
2087 if (ARDOUR::AudioEngine::instance()->running()) {
2088 ARDOUR_UI::instance()->disconnect_from_engine ();
2090 ARDOUR_UI::instance()->reconnect_to_engine ();
2095 EngineControl::calibrate_audio_latency ()
2097 _measure_midi.reset ();
2098 have_lm_results = false;
2099 lm_use_button.set_sensitive (false);
2100 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2101 notebook.set_current_page (latency_tab);
2105 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2108 have_lm_results = false;
2109 lm_use_button.set_sensitive (false);
2110 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2111 notebook.set_current_page (latency_tab);
2115 EngineControl::configure_midi_devices ()
2117 notebook.set_current_page (midi_tab);