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>
29 #include <gtkmm/messagedialog.h>
31 #include "pbd/error.h"
32 #include "pbd/xml++.h"
34 #include <gtkmm/stock.h>
35 #include <gtkmm2ext/utils.h>
37 #include "ardour/audio_backend.h"
38 #include "ardour/audioengine.h"
39 #include "ardour/rc_configuration.h"
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
44 #include "engine_dialog.h"
49 using namespace Gtkmm2ext;
53 EngineControl::EngineControl ()
54 : input_latency_adjustment (0, 0, 99999, 1)
55 , input_latency (input_latency_adjustment)
56 , output_latency_adjustment (0, 0, 99999, 1)
57 , output_latency (output_latency_adjustment)
58 , input_channels_adjustment (2, 0, 256, 1)
59 , input_channels (input_channels_adjustment)
60 , output_channels_adjustment (2, 0, 256, 1)
61 , output_channels (output_channels_adjustment)
62 , ports_adjustment (128, 8, 1024, 1, 16)
63 , ports_spinner (ports_adjustment)
64 , realtime_button (_("Realtime"))
67 , options_packer (4, 2)
68 , device_packer (4, 2)
71 , options_packer (14, 2)
72 , device_packer (6, 2)
75 using namespace Notebook_Helpers;
77 vector<string> strings;
82 /* basic parameters */
84 basic_packer.set_spacings (6);
88 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
89 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
90 strings.push_back ((*b)->name);
93 set_popdown_strings (backend_combo, strings);
94 backend_combo.set_active_text (strings.front());
96 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
99 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
102 strings.push_back (_("Playback/recording on 1 device"));
103 strings.push_back (_("Playback/recording on 2 devices"));
104 strings.push_back (_("Playback only"));
105 strings.push_back (_("Recording only"));
106 set_popdown_strings (audio_mode_combo, strings);
107 audio_mode_combo.set_active_text (strings.front());
109 audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
110 audio_mode_changed ();
113 strings.push_back (_("None"));
114 strings.push_back (_("seq"));
115 strings.push_back (_("raw"));
116 set_popdown_strings (midi_driver_combo, strings);
117 midi_driver_combo.set_active_text (strings.front ());
121 label = manage (left_aligned_label (_("Audio Driver:")));
122 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
123 basic_packer.attach (backend_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
126 label = manage (left_aligned_label (_("Driver:")));
127 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
128 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
131 label = manage (left_aligned_label (_("Audio Interface:")));
132 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
133 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
136 label = manage (left_aligned_label (_("Sample rate:")));
137 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
138 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
141 label = manage (left_aligned_label (_("Buffer size:")));
142 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
143 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
146 label = manage (left_aligned_label (_("Approximate latency:")));
147 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
148 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
151 sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
152 buffer_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
155 /* no audio mode with CoreAudio, its duplex or nuthin' */
157 #if !defined(__APPLE__) && !defined(__FreeBSD__)
158 label = manage (left_aligned_label (_("Audio mode:")));
159 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
160 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
164 interface_combo.set_size_request (250, -1);
165 input_device_combo.set_size_request (250, -1);
166 output_device_combo.set_size_request (250, -1);
168 interface_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::interface_changed));
172 options_packer.set_spacings (6);
175 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
178 realtime_button.set_active (true);
180 label = manage (left_aligned_label (_("Number of ports:")));
181 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
182 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 label = manage (left_aligned_label (_("MIDI driver:")));
186 options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
187 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
190 #if !defined(__APPLE__) && !defined(__FreeBSD__)
191 label = manage (left_aligned_label (_("Dither:")));
192 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
193 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
197 /* device settings */
199 device_packer.set_spacings (6);
202 #if !defined(__APPLE__) && !defined(__FreeBSD__)
203 label = manage (left_aligned_label (_("Input device:")));
204 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
205 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
207 label = manage (left_aligned_label (_("Output device:")));
208 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
209 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
212 label = manage (left_aligned_label (_("Hardware input latency:")));
213 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
214 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
215 label = manage (left_aligned_label (_("samples")));
216 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
218 label = manage (left_aligned_label (_("Hardware output latency:")));
219 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
220 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
221 label = manage (left_aligned_label (_("samples")));
222 device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
225 basic_hbox.pack_start (basic_packer, false, false);
226 options_hbox.pack_start (options_packer, false, false);
228 device_packer.set_border_width (12);
229 options_packer.set_border_width (12);
230 basic_packer.set_border_width (12);
232 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
233 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
234 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
235 notebook.set_border_width (12);
237 set_border_width (12);
238 pack_start (notebook);
240 /* Pick up any existing audio setup configuration, if appropriate */
242 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
245 set_state (*audio_setup);
249 EngineControl::~EngineControl ()
255 EngineControl::backend_changed ()
257 string backend_name = backend_combo.get_active_text();
258 boost::shared_ptr<ARDOUR::AudioBackend> backend;
260 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
265 if (backend->requires_driver_selection()) {
266 vector<string> drivers = backend->enumerate_drivers();
267 set_popdown_strings (driver_combo, drivers);
268 driver_combo.set_active_text (drivers.front());
276 EngineControl::list_devices ()
278 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
281 /* now fill out devices, mark sample rates, buffer sizes insensitive */
283 vector<string> devices = backend->enumerate_devices ();
285 set_popdown_strings (interface_combo, devices);
286 interface_combo.set_active_text (devices.front());
287 set_popdown_strings (input_device_combo, devices);
288 input_device_combo.set_active_text (devices.front());
289 set_popdown_strings (output_device_combo, devices);
290 output_device_combo.set_active_text (devices.front());
292 interface_changed ();
296 EngineControl::driver_changed ()
298 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
301 backend->set_driver (driver_combo.get_active_text());
306 EngineControl::interface_changed ()
308 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
310 string device_name = interface_combo.get_active_text ();
315 vector<float> sr = backend->available_sample_rates (device_name);
316 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
318 if (fmod (*x, 1000.0f)) {
319 snprintf (buf, sizeof (buf), "%.1f kHz", (*x)/1000.0);
321 snprintf (buf, sizeof (buf), "%.0f kHz", (*x)/1000.0);
326 set_popdown_strings (sample_rate_combo, s);
327 sample_rate_combo.set_active_text (s.front());
332 vector<uint32_t> bs = backend->available_buffer_sizes(device_name);
333 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
335 snprintf (buf, sizeof (buf), "%u", *x);
339 set_popdown_strings (buffer_size_combo, s);
340 buffer_size_combo.set_active_text (s.front());
344 EngineControl::redisplay_latency ()
346 uint32_t rate = get_rate();
347 float period_size = atof (buffer_size_combo.get_active_text());
350 snprintf (buf, sizeof(buf), "%.1fmsec", (2 * period_size) / (rate/1000.0));
352 latency_label.set_text (buf);
353 latency_label.set_alignment (0, 0.5);
357 EngineControl::audio_mode_changed ()
359 std::string str = audio_mode_combo.get_active_text();
361 if (str == _("Playback/recording on 1 device")) {
362 input_device_combo.set_sensitive (false);
363 output_device_combo.set_sensitive (false);
364 } else if (str == _("Playback/recording on 2 devices")) {
365 input_device_combo.set_sensitive (true);
366 output_device_combo.set_sensitive (true);
367 } else if (str == _("Playback only")) {
368 output_device_combo.set_sensitive (true);
369 input_device_combo.set_sensitive (false);
370 } else if (str == _("Recording only")) {
371 input_device_combo.set_sensitive (true);
372 output_device_combo.set_sensitive (false);
377 EngineControl::get_state ()
379 XMLNode* root = new XMLNode ("AudioSetup");
384 child = new XMLNode ("periods");
385 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
386 root->add_child_nocopy (*child);
388 child = new XMLNode ("ports");
389 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
390 root->add_child_nocopy (*child);
392 child = new XMLNode ("inlatency");
393 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
394 root->add_child_nocopy (*child);
396 child = new XMLNode ("outlatency");
397 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
398 root->add_child_nocopy (*child);
400 child = new XMLNode ("realtime");
401 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
402 root->add_child_nocopy (*child);
404 child = new XMLNode ("nomemorylock");
405 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
406 root->add_child_nocopy (*child);
408 child = new XMLNode ("unlockmemory");
409 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
410 root->add_child_nocopy (*child);
412 child = new XMLNode ("softmode");
413 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
414 root->add_child_nocopy (*child);
416 child = new XMLNode ("force16bit");
417 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
418 root->add_child_nocopy (*child);
420 child = new XMLNode ("hwmonitor");
421 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
422 root->add_child_nocopy (*child);
424 child = new XMLNode ("hwmeter");
425 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
426 root->add_child_nocopy (*child);
428 child = new XMLNode ("verbose");
429 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
430 root->add_child_nocopy (*child);
432 child = new XMLNode ("samplerate");
433 child->add_property ("val", sample_rate_combo.get_active_text());
434 root->add_child_nocopy (*child);
436 child = new XMLNode ("periodsize");
437 child->add_property ("val", period_size_combo.get_active_text());
438 root->add_child_nocopy (*child);
440 child = new XMLNode ("serverpath");
441 child->add_property ("val", serverpath_combo.get_active_text());
442 root->add_child_nocopy (*child);
444 child = new XMLNode ("driver");
445 child->add_property ("val", driver_combo.get_active_text());
446 root->add_child_nocopy (*child);
448 child = new XMLNode ("interface");
449 child->add_property ("val", interface_combo.get_active_text());
450 root->add_child_nocopy (*child);
452 child = new XMLNode ("timeout");
453 child->add_property ("val", timeout_combo.get_active_text());
454 root->add_child_nocopy (*child);
456 child = new XMLNode ("dither");
457 child->add_property ("val", dither_mode_combo.get_active_text());
458 root->add_child_nocopy (*child);
460 child = new XMLNode ("audiomode");
461 child->add_property ("val", audio_mode_combo.get_active_text());
462 root->add_child_nocopy (*child);
464 child = new XMLNode ("inputdevice");
465 child->add_property ("val", input_device_combo.get_active_text());
466 root->add_child_nocopy (*child);
468 child = new XMLNode ("outputdevice");
469 child->add_property ("val", output_device_combo.get_active_text());
470 root->add_child_nocopy (*child);
472 child = new XMLNode ("mididriver");
473 child->add_property ("val", midi_driver_combo.get_active_text());
474 root->add_child_nocopy (*child);
480 EngineControl::set_state (const XMLNode& root)
484 XMLNodeConstIterator citer;
486 XMLProperty* prop = NULL;
487 bool using_dummy = false;
488 bool using_ffado = false;
493 if ( (child = root.child ("driver"))){
494 prop = child->property("val");
496 if (prop && (prop->value() == "Dummy") ) {
499 if (prop && (prop->value() == "FFADO") ) {
505 clist = root.children();
507 for (citer = clist.begin(); citer != clist.end(); ++citer) {
511 prop = child->property ("val");
513 if (!prop || prop->value().empty()) {
515 if (((using_dummy || using_ffado)
516 && ( child->name() == "interface"
517 || child->name() == "inputdevice"
518 || child->name() == "outputdevice"))
519 || child->name() == "timeout")
524 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
528 strval = prop->value();
530 /* adjustments/spinners */
532 if (child->name() == "periods") {
534 periods_adjustment.set_value(val);
535 } else if (child->name() == "ports") {
537 ports_adjustment.set_value(val);
538 } else if (child->name() == "inlatency") {
540 input_latency.set_value(val);
541 } else if (child->name() == "outlatency") {
543 output_latency.set_value(val);
548 else if (child->name() == "realtime") {
550 realtime_button.set_active(val);
551 } else if (child->name() == "nomemorylock") {
553 no_memory_lock_button.set_active(val);
554 } else if (child->name() == "unlockmemory") {
556 unlock_memory_button.set_active(val);
557 } else if (child->name() == "softmode") {
559 soft_mode_button.set_active(val);
560 } else if (child->name() == "force16bit") {
562 force16bit_button.set_active(val);
563 } else if (child->name() == "hwmonitor") {
565 hw_monitor_button.set_active(val);
566 } else if (child->name() == "hwmeter") {
568 hw_meter_button.set_active(val);
569 } else if (child->name() == "verbose") {
571 verbose_output_button.set_active(val);
576 else if (child->name() == "samplerate") {
577 sample_rate_combo.set_active_text(strval);
578 } else if (child->name() == "periodsize") {
579 period_size_combo.set_active_text(strval);
580 } else if (child->name() == "serverpath") {
582 /* only attempt to set this if we have bothered to look
583 up server names already. otherwise this is all
584 redundant (actually, all of this dialog/widget
585 is redundant in that case ...)
588 if (!server_strings.empty()) {
589 /* do not allow us to use a server path that doesn't
590 exist on this system. this handles cases where
591 the user has an RC file listing a serverpath
592 from some other machine.
594 vector<string>::iterator x;
595 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
600 if (x != server_strings.end()) {
601 serverpath_combo.set_active_text (strval);
603 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
609 } else if (child->name() == "driver") {
610 driver_combo.set_active_text(strval);
611 } else if (child->name() == "interface") {
612 interface_combo.set_active_text(strval);
613 } else if (child->name() == "timeout") {
614 timeout_combo.set_active_text(strval);
615 } else if (child->name() == "dither") {
616 dither_mode_combo.set_active_text(strval);
617 } else if (child->name() == "audiomode") {
618 audio_mode_combo.set_active_text(strval);
619 } else if (child->name() == "inputdevice") {
620 input_device_combo.set_active_text(strval);
621 } else if (child->name() == "outputdevice") {
622 output_device_combo.set_active_text(strval);
623 } else if (child->name() == "mididriver") {
624 midi_driver_combo.set_active_text(strval);
631 EngineControl::setup_engine (bool start)
633 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
636 /* grab the parameters from the GUI and apply them */
639 if (backend->requires_driver_selection()) {
640 if (backend->set_driver (get_driver())) {
645 if (backend->set_device_name (get_device_name())) {
649 if (backend->set_sample_rate (get_rate())) {
650 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
653 if (backend->set_buffer_size (get_buffer_size())) {
654 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
657 if (backend->set_input_channels (get_input_channels())) {
658 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
661 if (backend->set_output_channels (get_output_channels())) {
662 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
665 if (backend->set_systemic_input_latency (get_input_latency())) {
666 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
669 if (backend->set_systemic_output_latency (get_output_latency())) {
670 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
675 return ARDOUR::AudioEngine::instance()->start();
681 cerr << "exception thrown...\n";
687 EngineControl::get_rate () const
689 double r = atof (sample_rate_combo.get_active_text ());
690 /* the string may have been translated with an abbreviation for
691 * thousands, so use a crude heuristic to fix this.
700 EngineControl::get_buffer_size () const
702 string txt = buffer_size_combo.get_active_text ();
705 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
713 EngineControl::get_input_channels() const
715 return (uint32_t) input_channels_adjustment.get_value();
719 EngineControl::get_output_channels() const
721 return (uint32_t) output_channels_adjustment.get_value();
725 EngineControl::get_input_latency() const
727 return (uint32_t) input_latency_adjustment.get_value();
731 EngineControl::get_output_latency() const
733 return (uint32_t) output_latency_adjustment.get_value();
737 EngineControl::get_driver () const
739 return driver_combo.get_active_text ();
743 EngineControl::get_device_name () const
745 return interface_combo.get_active_text ();