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 for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
1344 backend_combo.set_active_text ((*i)->backend);
1345 driver_combo.set_active_text ((*i)->driver);
1346 device_combo.set_active_text ((*i)->device);
1347 sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
1348 buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
1349 input_latency.set_value ((*i)->input_latency);
1350 output_latency.set_value ((*i)->output_latency);
1351 midi_option_combo.set_active_text ((*i)->midi_option);
1359 EngineControl::push_state_to_backend (bool start)
1361 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1367 /* figure out what is going to change */
1369 bool restart_required = false;
1370 bool was_running = ARDOUR::AudioEngine::instance()->running();
1371 bool change_driver = false;
1372 bool change_device = false;
1373 bool change_rate = false;
1374 bool change_bufsize = false;
1375 bool change_latency = false;
1376 bool change_channels = false;
1377 bool change_midi = false;
1379 uint32_t ochan = get_output_channels ();
1380 uint32_t ichan = get_input_channels ();
1382 if (_have_control) {
1384 if (started_at_least_once) {
1386 /* we can control the backend */
1388 if (backend->requires_driver_selection()) {
1389 if (get_driver() != backend->driver_name()) {
1390 change_driver = true;
1394 if (get_device_name() != backend->device_name()) {
1395 change_device = true;
1398 if (get_rate() != backend->sample_rate()) {
1402 if (get_buffer_size() != backend->buffer_size()) {
1403 change_bufsize = true;
1406 if (get_midi_option() != backend->midi_option()) {
1410 /* zero-requested channels means "all available" */
1413 ichan = backend->input_channels();
1417 ochan = backend->output_channels();
1420 if (ichan != backend->input_channels()) {
1421 change_channels = true;
1424 if (ochan != backend->output_channels()) {
1425 change_channels = true;
1428 if (get_input_latency() != backend->systemic_input_latency() ||
1429 get_output_latency() != backend->systemic_output_latency()) {
1430 change_latency = true;
1433 /* backend never started, so we have to force a group
1436 change_device = true;
1437 if (backend->requires_driver_selection()) {
1438 change_driver = true;
1441 change_bufsize = true;
1442 change_channels = true;
1443 change_latency = true;
1449 /* we have no control over the backend, meaning that we can
1450 * only possibly change sample rate and buffer size.
1454 if (get_rate() != backend->sample_rate()) {
1455 change_bufsize = true;
1458 if (get_buffer_size() != backend->buffer_size()) {
1459 change_bufsize = true;
1463 if (!_have_control) {
1465 /* We do not have control over the backend, so the best we can
1466 * do is try to change the sample rate and/or bufsize and get
1470 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1474 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1479 backend->set_sample_rate (get_rate());
1482 if (change_bufsize) {
1483 backend->set_buffer_size (get_buffer_size());
1487 if (ARDOUR::AudioEngine::instance()->start ()) {
1488 error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
1498 /* determine if we need to stop the backend before changing parameters */
1500 if (change_driver || change_device || change_channels || change_latency ||
1501 (change_rate && !backend->can_change_sample_rate_when_running()) ||
1503 (change_bufsize && !backend->can_change_buffer_size_when_running())) {
1504 restart_required = true;
1506 restart_required = false;
1511 if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
1512 /* no changes in any parameters that absolutely require a
1513 * restart, so check those that might be changeable without a
1517 if (change_rate && !backend->can_change_sample_rate_when_running()) {
1518 /* can't do this while running ... */
1519 restart_required = true;
1522 if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
1523 /* can't do this while running ... */
1524 restart_required = true;
1530 if (restart_required) {
1531 if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
1538 if (change_driver && backend->set_driver (get_driver())) {
1539 error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
1542 if (change_device && backend->set_device_name (get_device_name())) {
1543 error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
1546 if (change_rate && backend->set_sample_rate (get_rate())) {
1547 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
1550 if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
1551 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
1555 if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
1556 if (backend->set_input_channels (get_input_channels())) {
1557 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
1560 if (backend->set_output_channels (get_output_channels())) {
1561 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
1565 if (change_latency) {
1566 if (backend->set_systemic_input_latency (get_input_latency())) {
1567 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
1570 if (backend->set_systemic_output_latency (get_output_latency())) {
1571 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
1577 backend->set_midi_option (get_midi_option());
1581 for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
1582 if (_measure_midi) {
1583 if (*p == _measure_midi) {
1584 backend->set_midi_device_enabled ((*p)->name, true);
1586 backend->set_midi_device_enabled ((*p)->name, false);
1590 backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
1591 if (backend->can_set_systemic_midi_latencies()) {
1592 backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
1593 backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
1598 if (start || (was_running && restart_required)) {
1599 if (ARDOUR_UI::instance()->reconnect_to_engine()) {
1610 EngineControl::post_push ()
1612 /* get a pointer to the current state object, creating one if
1616 State state = get_saved_state_for_currently_displayed_backend_and_device ();
1619 state = save_state ();
1625 for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
1626 (*i)->active = false;
1629 /* mark this one active (to be used next time the dialog is
1633 state->active = true;
1635 if (_have_control) { // XXX
1636 manage_control_app_sensitivity ();
1639 /* schedule a redisplay of MIDI ports */
1640 //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
1645 EngineControl::get_rate () const
1647 float r = atof (sample_rate_combo.get_active_text ());
1648 /* the string may have been translated with an abbreviation for
1649 * thousands, so use a crude heuristic to fix this.
1659 EngineControl::get_buffer_size () const
1661 string txt = buffer_size_combo.get_active_text ();
1664 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
1672 EngineControl::get_midi_option () const
1674 return midi_option_combo.get_active_text();
1678 EngineControl::get_input_channels() const
1680 return (uint32_t) input_channels_adjustment.get_value();
1684 EngineControl::get_output_channels() const
1686 return (uint32_t) output_channels_adjustment.get_value();
1690 EngineControl::get_input_latency() const
1692 return (uint32_t) input_latency_adjustment.get_value();
1696 EngineControl::get_output_latency() const
1698 return (uint32_t) output_latency_adjustment.get_value();
1702 EngineControl::get_backend () const
1704 return backend_combo.get_active_text ();
1708 EngineControl::get_driver () const
1710 if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
1711 return driver_combo.get_active_text ();
1718 EngineControl::get_device_name () const
1720 return device_combo.get_active_text ();
1724 EngineControl::control_app_button_clicked ()
1726 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1732 backend->launch_control_app ();
1736 EngineControl::manage_control_app_sensitivity ()
1738 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
1744 string appname = backend->control_app_name();
1746 if (appname.empty()) {
1747 control_app_button.set_sensitive (false);
1749 control_app_button.set_sensitive (true);
1754 EngineControl::set_desired_sample_rate (uint32_t sr)
1756 _desired_sample_rate = sr;
1761 EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
1763 if (page_num == 0) {
1764 cancel_button->set_sensitive (true);
1765 ok_button->set_sensitive (true);
1766 apply_button->set_sensitive (true);
1767 _measure_midi.reset();
1769 cancel_button->set_sensitive (false);
1770 ok_button->set_sensitive (false);
1771 apply_button->set_sensitive (false);
1774 if (page_num == midi_tab) {
1776 refresh_midi_display ();
1779 if (page_num == latency_tab) {
1782 if (ARDOUR::AudioEngine::instance()->running()) {
1783 // TODO - mark as 'stopped for latency
1784 ARDOUR_UI::instance()->disconnect_from_engine ();
1788 PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
1790 /* save any existing latency values */
1792 uint32_t il = (uint32_t) input_latency.get_value ();
1793 uint32_t ol = (uint32_t) input_latency.get_value ();
1795 /* reset to zero so that our new test instance
1796 will be clean of any existing latency measures.
1798 NB. this should really be done by the backend
1799 when stated for latency measurement.
1802 input_latency.set_value (0);
1803 output_latency.set_value (0);
1805 push_state_to_backend (false);
1809 input_latency.set_value (il);
1810 output_latency.set_value (ol);
1813 // This should be done in push_state_to_backend()
1814 if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
1815 disable_latency_tab ();
1818 enable_latency_tab ();
1822 end_latency_detection ();
1823 ARDOUR::AudioEngine::instance()->stop_latency_detection();
1828 /* latency measurement */
1831 EngineControl::check_audio_latency_measurement ()
1833 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
1835 if (mtdm->resolve () < 0) {
1836 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1840 if (mtdm->err () > 0.3) {
1846 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1848 if (sample_rate == 0) {
1849 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1850 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1854 int frames_total = mtdm->del();
1855 int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1857 snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
1858 _("Detected roundtrip latency: "),
1859 frames_total, frames_total * 1000.0f/sample_rate,
1860 _("Systemic latency: "),
1861 extra, extra * 1000.0f/sample_rate);
1865 if (mtdm->err () > 0.2) {
1867 strcat (buf, _("(signal detection error)"));
1873 strcat (buf, _("(inverted - bad wiring)"));
1878 have_lm_results = true;
1879 end_latency_detection ();
1880 lm_use_button.set_sensitive (true);
1884 lm_results.set_markup (string_compose (results_markup, buf));
1890 EngineControl::check_midi_latency_measurement ()
1892 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
1894 if (!mididm->have_signal () || mididm->latency () == 0) {
1895 lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
1900 ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
1902 if (sample_rate == 0) {
1903 lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
1904 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1908 ARDOUR::framecnt_t frames_total = mididm->latency();
1909 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
1910 snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
1911 _("Detected roundtrip latency: "),
1912 frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
1913 _("Systemic latency: "),
1914 extra, extra * 1000.0f / sample_rate);
1918 if (!mididm->ok ()) {
1920 strcat (buf, _("(averaging)"));
1924 if (mididm->deviation () > 50.0) {
1926 strcat (buf, _("(too large jitter)"));
1928 } else if (mididm->deviation () > 10.0) {
1930 strcat (buf, _("(large jitter)"));
1934 have_lm_results = true;
1935 end_latency_detection ();
1936 lm_use_button.set_sensitive (true);
1938 } else if (mididm->processed () > 400) {
1939 have_lm_results = false;
1940 end_latency_detection ();
1941 lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
1945 lm_results.set_markup (string_compose (results_markup, buf));
1951 EngineControl::start_latency_detection ()
1953 ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
1954 ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
1956 if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
1957 lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
1958 if (_measure_midi) {
1959 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
1961 latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
1963 lm_measure_label.set_text (_("Cancel"));
1964 have_lm_results = false;
1965 lm_use_button.set_sensitive (false);
1966 lm_input_channel_combo.set_sensitive (false);
1967 lm_output_channel_combo.set_sensitive (false);
1973 EngineControl::end_latency_detection ()
1975 latency_timeout.disconnect ();
1976 ARDOUR::AudioEngine::instance()->stop_latency_detection ();
1977 lm_measure_label.set_text (_("Measure"));
1978 if (!have_lm_results) {
1979 lm_use_button.set_sensitive (false);
1981 lm_input_channel_combo.set_sensitive (true);
1982 lm_output_channel_combo.set_sensitive (true);
1987 EngineControl::latency_button_clicked ()
1990 start_latency_detection ();
1992 end_latency_detection ();
1997 EngineControl::use_latency_button_clicked ()
1999 if (_measure_midi) {
2000 ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
2004 ARDOUR::framecnt_t frames_total = mididm->latency();
2005 ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
2006 uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
2007 _measure_midi->input_latency = one_way;
2008 _measure_midi->output_latency = one_way;
2009 notebook.set_current_page (midi_tab);
2011 MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
2017 double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
2018 one_way = std::max (0., one_way);
2020 input_latency_adjustment.set_value (one_way);
2021 output_latency_adjustment.set_value (one_way);
2023 /* back to settings page */
2024 notebook.set_current_page (0);
2030 EngineControl::on_delete_event (GdkEventAny* ev)
2032 if (notebook.get_current_page() == 2) {
2033 /* currently on latency tab - be sure to clean up */
2034 end_latency_detection ();
2036 return ArdourDialog::on_delete_event (ev);
2040 EngineControl::engine_running ()
2042 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2045 buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
2046 sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
2048 buffer_size_combo.set_sensitive (true);
2049 sample_rate_combo.set_sensitive (true);
2051 connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
2053 started_at_least_once = true;
2057 EngineControl::engine_stopped ()
2059 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
2062 buffer_size_combo.set_sensitive (false);
2063 connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
2065 sample_rate_combo.set_sensitive (true);
2066 buffer_size_combo.set_sensitive (true);
2070 EngineControl::connect_disconnect_click()
2072 if (ARDOUR::AudioEngine::instance()->running()) {
2073 ARDOUR_UI::instance()->disconnect_from_engine ();
2075 ARDOUR_UI::instance()->reconnect_to_engine ();
2080 EngineControl::calibrate_audio_latency ()
2082 _measure_midi.reset ();
2083 have_lm_results = false;
2084 lm_use_button.set_sensitive (false);
2085 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2086 notebook.set_current_page (latency_tab);
2090 EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
2093 have_lm_results = false;
2094 lm_use_button.set_sensitive (false);
2095 lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
2096 notebook.set_current_page (latency_tab);
2100 EngineControl::configure_midi_devices ()
2102 notebook.set_current_page (midi_tab);