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 <gtkmm/notebook.h>
36 #include <gtkmm2ext/utils.h>
38 #include "ardour/audio_backend.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/rc_configuration.h"
42 #include "pbd/convert.h"
43 #include "pbd/error.h"
45 #include "engine_dialog.h"
50 using namespace Gtkmm2ext;
54 EngineControl::EngineControl ()
55 : input_latency_adjustment (0, 0, 99999, 1)
56 , input_latency (input_latency_adjustment)
57 , output_latency_adjustment (0, 0, 99999, 1)
58 , output_latency (output_latency_adjustment)
59 , input_channels_adjustment (2, 0, 256, 1)
60 , input_channels (input_channels_adjustment)
61 , output_channels_adjustment (2, 0, 256, 1)
62 , output_channels (output_channels_adjustment)
63 , ports_adjustment (128, 8, 1024, 1, 16)
64 , ports_spinner (ports_adjustment)
65 , realtime_button (_("Realtime"))
72 using namespace Notebook_Helpers;
74 vector<string> strings;
79 /* basic parameters */
81 basic_packer.set_spacings (6);
85 vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
86 for (vector<const ARDOUR::AudioBackendInfo*>::const_iterator b = backends.begin(); b != backends.end(); ++b) {
87 strings.push_back ((*b)->name);
90 set_popdown_strings (backend_combo, strings);
91 backend_combo.set_active_text (strings.front());
93 backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
96 driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
99 strings.push_back (_("None"));
101 strings.push_back (_("coremidi"));
103 strings.push_back (_("seq"));
104 strings.push_back (_("raw"));
106 set_popdown_strings (midi_driver_combo, strings);
107 midi_driver_combo.set_active_text (strings.front ());
111 label = manage (left_aligned_label (_("Audio System:")));
112 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
113 basic_packer.attach (backend_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
116 label = manage (left_aligned_label (_("Driver:")));
117 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
118 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
121 label = manage (left_aligned_label (_("Device:")));
122 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
123 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
126 label = manage (left_aligned_label (_("Sample rate:")));
127 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
128 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
131 label = manage (left_aligned_label (_("Buffer size:")));
132 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
133 basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
136 label = manage (left_aligned_label (_("Hardware input latency:")));
137 basic_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
138 basic_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
139 label = manage (left_aligned_label (_("samples")));
140 basic_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
143 label = manage (left_aligned_label (_("Hardware output latency:")));
144 basic_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
145 basic_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
146 label = manage (left_aligned_label (_("samples")));
147 basic_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
150 sr_connection = sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::reshow_buffer_sizes));
152 interface_combo.set_size_request (250, -1);
153 input_device_combo.set_size_request (250, -1);
154 output_device_combo.set_size_request (250, -1);
156 interface_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::interface_changed));
158 basic_hbox.pack_start (basic_packer, false, false);
160 basic_packer.set_border_width (12);
161 midi_packer.set_border_width (12);
163 notebook.pages().push_back (TabElem (basic_hbox, _("Audio System Settings")));
164 notebook.pages().push_back (TabElem (midi_hbox, _("MIDI Settings")));
165 notebook.set_border_width (12);
167 notebook.set_tab_pos (POS_RIGHT);
168 notebook.show_all ();
170 notebook.set_name ("SettingsNotebook");
172 set_border_width (12);
173 pack_start (notebook);
175 /* Pick up any existing audio setup configuration, if appropriate */
177 XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
180 set_state (*audio_setup);
184 EngineControl::~EngineControl ()
190 EngineControl::backend_changed ()
192 string backend_name = backend_combo.get_active_text();
193 boost::shared_ptr<ARDOUR::AudioBackend> backend;
195 if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
200 if (backend->requires_driver_selection()) {
201 vector<string> drivers = backend->enumerate_drivers();
202 driver_combo.set_sensitive (true);
203 set_popdown_strings (driver_combo, drivers);
204 driver_combo.set_active_text (drivers.front());
207 driver_combo.set_sensitive (false);
213 EngineControl::list_devices ()
215 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
218 /* now fill out devices, mark sample rates, buffer sizes insensitive */
220 vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
222 /* NOTE: Ardour currently does not display the "available" field of the
225 * Doing so would require a different GUI widget than the combo
226 * box/popdown that we currently use, since it has no way to list
227 * items that are not selectable. Something more like a popup menu,
228 * which could have unselectable items, would be appropriate.
231 vector<string> available_devices;
233 for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
234 available_devices.push_back (i->name);
237 set_popdown_strings (interface_combo, available_devices);
238 set_popdown_strings (input_device_combo, available_devices);
239 set_popdown_strings (output_device_combo, available_devices);
241 if (!available_devices.empty()) {
242 interface_combo.set_active_text (available_devices.front());
243 input_device_combo.set_active_text (available_devices.front());
244 output_device_combo.set_active_text (available_devices.front());
247 interface_changed ();
251 EngineControl::driver_changed ()
253 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
256 backend->set_driver (driver_combo.get_active_text());
261 EngineControl::interface_changed ()
263 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
265 string device_name = interface_combo.get_active_text ();
268 /* don't allow programmatic change to sample_rate_combo to cause a
269 recursive call to this method.
272 sr_connection.block ();
276 vector<float> sr = backend->available_sample_rates (device_name);
277 for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
279 if (fmod (*x, 1000.0f)) {
280 snprintf (buf, sizeof (buf), "%.1f kHz", (*x)/1000.0);
282 snprintf (buf, sizeof (buf), "%.0f kHz", (*x)/1000.0);
287 set_popdown_strings (sample_rate_combo, s);
288 sample_rate_combo.set_active_text (s.front());
290 reshow_buffer_sizes ();
292 sr_connection.unblock ();
296 EngineControl::reshow_buffer_sizes ()
298 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
300 string device_name = interface_combo.get_active_text ();
306 vector<uint32_t> bs = backend->available_buffer_sizes(device_name);
307 uint32_t rate = get_rate();
309 for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
311 /* Translators: "samples" is ALWAYS plural here, so we do not
312 need singular form as well. Same for msecs.
314 snprintf (buf, sizeof (buf), _("%u samples (%.1f msecs)"), *x, (2 * (*x)) / (rate/1000.0));
318 set_popdown_strings (buffer_size_combo, s);
319 buffer_size_combo.set_active_text (s.front());
323 EngineControl::audio_mode_changed ()
325 std::string str = audio_mode_combo.get_active_text();
327 if (str == _("Playback/recording on 1 device")) {
328 input_device_combo.set_sensitive (false);
329 output_device_combo.set_sensitive (false);
330 } else if (str == _("Playback/recording on 2 devices")) {
331 input_device_combo.set_sensitive (true);
332 output_device_combo.set_sensitive (true);
333 } else if (str == _("Playback only")) {
334 output_device_combo.set_sensitive (true);
335 input_device_combo.set_sensitive (false);
336 } else if (str == _("Recording only")) {
337 input_device_combo.set_sensitive (true);
338 output_device_combo.set_sensitive (false);
343 EngineControl::get_state ()
345 XMLNode* root = new XMLNode ("AudioSetup");
350 child = new XMLNode ("periods");
351 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
352 root->add_child_nocopy (*child);
354 child = new XMLNode ("ports");
355 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
356 root->add_child_nocopy (*child);
358 child = new XMLNode ("inlatency");
359 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
360 root->add_child_nocopy (*child);
362 child = new XMLNode ("outlatency");
363 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
364 root->add_child_nocopy (*child);
366 child = new XMLNode ("realtime");
367 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
368 root->add_child_nocopy (*child);
370 child = new XMLNode ("nomemorylock");
371 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
372 root->add_child_nocopy (*child);
374 child = new XMLNode ("unlockmemory");
375 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
376 root->add_child_nocopy (*child);
378 child = new XMLNode ("softmode");
379 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
380 root->add_child_nocopy (*child);
382 child = new XMLNode ("force16bit");
383 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
384 root->add_child_nocopy (*child);
386 child = new XMLNode ("hwmonitor");
387 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
388 root->add_child_nocopy (*child);
390 child = new XMLNode ("hwmeter");
391 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
392 root->add_child_nocopy (*child);
394 child = new XMLNode ("verbose");
395 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
396 root->add_child_nocopy (*child);
398 child = new XMLNode ("samplerate");
399 child->add_property ("val", sample_rate_combo.get_active_text());
400 root->add_child_nocopy (*child);
402 child = new XMLNode ("periodsize");
403 child->add_property ("val", period_size_combo.get_active_text());
404 root->add_child_nocopy (*child);
406 child = new XMLNode ("serverpath");
407 child->add_property ("val", serverpath_combo.get_active_text());
408 root->add_child_nocopy (*child);
410 child = new XMLNode ("driver");
411 child->add_property ("val", driver_combo.get_active_text());
412 root->add_child_nocopy (*child);
414 child = new XMLNode ("interface");
415 child->add_property ("val", interface_combo.get_active_text());
416 root->add_child_nocopy (*child);
418 child = new XMLNode ("timeout");
419 child->add_property ("val", timeout_combo.get_active_text());
420 root->add_child_nocopy (*child);
422 child = new XMLNode ("dither");
423 child->add_property ("val", dither_mode_combo.get_active_text());
424 root->add_child_nocopy (*child);
426 child = new XMLNode ("audiomode");
427 child->add_property ("val", audio_mode_combo.get_active_text());
428 root->add_child_nocopy (*child);
430 child = new XMLNode ("inputdevice");
431 child->add_property ("val", input_device_combo.get_active_text());
432 root->add_child_nocopy (*child);
434 child = new XMLNode ("outputdevice");
435 child->add_property ("val", output_device_combo.get_active_text());
436 root->add_child_nocopy (*child);
438 child = new XMLNode ("mididriver");
439 child->add_property ("val", midi_driver_combo.get_active_text());
440 root->add_child_nocopy (*child);
446 EngineControl::set_state (const XMLNode& root)
450 XMLNodeConstIterator citer;
452 XMLProperty* prop = NULL;
453 bool using_dummy = false;
454 bool using_ffado = false;
459 if ( (child = root.child ("driver"))){
460 prop = child->property("val");
462 if (prop && (prop->value() == "Dummy") ) {
465 if (prop && (prop->value() == "FFADO") ) {
471 clist = root.children();
473 for (citer = clist.begin(); citer != clist.end(); ++citer) {
477 prop = child->property ("val");
479 if (!prop || prop->value().empty()) {
481 if (((using_dummy || using_ffado)
482 && ( child->name() == "interface"
483 || child->name() == "inputdevice"
484 || child->name() == "outputdevice"))
485 || child->name() == "timeout")
490 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
494 strval = prop->value();
496 /* adjustments/spinners */
498 if (child->name() == "periods") {
500 periods_adjustment.set_value(val);
501 } else if (child->name() == "ports") {
503 ports_adjustment.set_value(val);
504 } else if (child->name() == "inlatency") {
506 input_latency.set_value(val);
507 } else if (child->name() == "outlatency") {
509 output_latency.set_value(val);
514 else if (child->name() == "realtime") {
516 realtime_button.set_active(val);
517 } else if (child->name() == "nomemorylock") {
519 no_memory_lock_button.set_active(val);
520 } else if (child->name() == "unlockmemory") {
522 unlock_memory_button.set_active(val);
523 } else if (child->name() == "softmode") {
525 soft_mode_button.set_active(val);
526 } else if (child->name() == "force16bit") {
528 force16bit_button.set_active(val);
529 } else if (child->name() == "hwmonitor") {
531 hw_monitor_button.set_active(val);
532 } else if (child->name() == "hwmeter") {
534 hw_meter_button.set_active(val);
535 } else if (child->name() == "verbose") {
537 verbose_output_button.set_active(val);
542 else if (child->name() == "samplerate") {
543 sample_rate_combo.set_active_text(strval);
544 } else if (child->name() == "periodsize") {
545 period_size_combo.set_active_text(strval);
546 } else if (child->name() == "serverpath") {
548 /* only attempt to set this if we have bothered to look
549 up server names already. otherwise this is all
550 redundant (actually, all of this dialog/widget
551 is redundant in that case ...)
554 if (!server_strings.empty()) {
555 /* do not allow us to use a server path that doesn't
556 exist on this system. this handles cases where
557 the user has an RC file listing a serverpath
558 from some other machine.
560 vector<string>::iterator x;
561 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
566 if (x != server_strings.end()) {
567 serverpath_combo.set_active_text (strval);
569 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
575 } else if (child->name() == "driver") {
576 driver_combo.set_active_text(strval);
577 } else if (child->name() == "interface") {
578 interface_combo.set_active_text(strval);
579 } else if (child->name() == "timeout") {
580 timeout_combo.set_active_text(strval);
581 } else if (child->name() == "dither") {
582 dither_mode_combo.set_active_text(strval);
583 } else if (child->name() == "audiomode") {
584 audio_mode_combo.set_active_text(strval);
585 } else if (child->name() == "inputdevice") {
586 input_device_combo.set_active_text(strval);
587 } else if (child->name() == "outputdevice") {
588 output_device_combo.set_active_text(strval);
589 } else if (child->name() == "mididriver") {
590 midi_driver_combo.set_active_text(strval);
597 EngineControl::setup_engine (bool start)
599 boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
602 /* grab the parameters from the GUI and apply them */
605 if (backend->requires_driver_selection()) {
606 if (backend->set_driver (get_driver())) {
611 if (backend->set_device_name (get_device_name())) {
615 if (backend->set_sample_rate (get_rate())) {
616 error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
619 if (backend->set_buffer_size (get_buffer_size())) {
620 error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
623 if (backend->set_input_channels (get_input_channels())) {
624 error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
627 if (backend->set_output_channels (get_output_channels())) {
628 error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
631 if (backend->set_systemic_input_latency (get_input_latency())) {
632 error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
635 if (backend->set_systemic_output_latency (get_output_latency())) {
636 error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
641 return ARDOUR::AudioEngine::instance()->start();
647 cerr << "exception thrown...\n";
653 EngineControl::get_rate () const
655 double r = atof (sample_rate_combo.get_active_text ());
656 /* the string may have been translated with an abbreviation for
657 * thousands, so use a crude heuristic to fix this.
666 EngineControl::get_buffer_size () const
668 string txt = buffer_size_combo.get_active_text ();
671 if (sscanf (txt.c_str(), "%d", &samples) != 1) {
679 EngineControl::get_input_channels() const
681 return (uint32_t) input_channels_adjustment.get_value();
685 EngineControl::get_output_channels() const
687 return (uint32_t) output_channels_adjustment.get_value();
691 EngineControl::get_input_latency() const
693 return (uint32_t) input_latency_adjustment.get_value();
697 EngineControl::get_output_latency() const
699 return (uint32_t) output_latency_adjustment.get_value();
703 EngineControl::get_driver () const
705 return driver_combo.get_active_text ();
709 EngineControl::get_device_name () const
711 return interface_combo.get_active_text ();