f0ac059a74abdffc438c899f2ec51c38ed78de98
[ardour.git] / gtk2_ardour / engine_dialog.cc
1 /*
2     Copyright (C) 2010 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #include <exception>
21 #include <vector>
22 #include <cmath>
23 #include <fstream>
24 #include <map>
25
26 #include <boost/scoped_ptr.hpp>
27
28 #include <glibmm.h>
29 #include <gtkmm/messagedialog.h>
30
31 #include "pbd/error.h"
32 #include "pbd/xml++.h"
33
34 #include <gtkmm/stock.h>
35 #include <gtkmm/notebook.h>
36 #include <gtkmm2ext/utils.h>
37
38 #include "ardour/audio_backend.h"
39 #include "ardour/audioengine.h"
40 #include "ardour/rc_configuration.h"
41
42 #include "pbd/convert.h"
43 #include "pbd/error.h"
44
45 #include "engine_dialog.h"
46 #include "i18n.h"
47
48 using namespace std;
49 using namespace Gtk;
50 using namespace Gtkmm2ext;
51 using namespace PBD;
52 using namespace Glib;
53
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"))
66 #ifdef __APPLE___
67         , basic_packer (6, 2)
68 #else
69         , basic_packer (9, 2)
70 #endif
71 {
72         using namespace Notebook_Helpers;
73         Label* label;
74         vector<string> strings;
75         int row = 0;
76
77         _used = false;
78
79         /* basic parameters */
80
81         basic_packer.set_spacings (6);
82
83         strings.clear ();
84
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);
88         }
89
90         set_popdown_strings (backend_combo, strings);
91         backend_combo.set_active_text (strings.front());
92
93         backend_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::backend_changed));
94         backend_changed ();
95
96         driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
97
98         strings.clear ();
99         strings.push_back (_("None"));
100 #ifdef __APPLE__
101         strings.push_back (_("coremidi"));
102 #else
103         strings.push_back (_("seq"));
104         strings.push_back (_("raw"));
105 #endif
106         set_popdown_strings (midi_driver_combo, strings);
107         midi_driver_combo.set_active_text (strings.front ());
108
109         row = 0;
110
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);
114         row++;
115
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);
119         row++;
120
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);
124         row++;
125
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);
129         row++;
130
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);
134         row++;
135
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);
141         ++row;
142
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);
148         ++row;
149
150         sr_connection = sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::reshow_buffer_sizes));
151
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);
155
156         interface_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::interface_changed));
157
158         basic_hbox.pack_start (basic_packer, false, false);
159
160         basic_packer.set_border_width (12);
161         midi_packer.set_border_width (12);
162
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);
166
167         notebook.set_tab_pos (POS_RIGHT);
168         notebook.show_all ();
169
170         notebook.set_name ("SettingsNotebook");
171
172         set_border_width (12);
173         pack_start (notebook);
174
175         /* Pick up any existing audio setup configuration, if appropriate */
176
177         XMLNode* audio_setup = ARDOUR::Config->extra_xml ("AudioSetup");
178         
179         if (audio_setup) {
180                 set_state (*audio_setup);
181         }
182 }
183
184 EngineControl::~EngineControl ()
185 {
186
187 }
188
189 void
190 EngineControl::backend_changed ()
191 {
192         string backend_name = backend_combo.get_active_text();
193         boost::shared_ptr<ARDOUR::AudioBackend> backend;
194
195         if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
196                 /* eh? */
197                 return;
198         }
199
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());
205                 driver_changed ();
206         } else {
207                 driver_combo.set_sensitive (false);
208                 list_devices ();
209         }
210 }
211
212 void
213 EngineControl::list_devices ()
214 {
215         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
216         assert (backend);
217
218         /* now fill out devices, mark sample rates, buffer sizes insensitive */
219             
220         vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
221         
222         /* NOTE: Ardour currently does not display the "available" field of the
223          * returned devices.
224          *
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.
229          */
230
231         vector<string> available_devices;
232
233         for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
234                 available_devices.push_back (i->name);
235         }
236
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);
240         
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());
245         }
246
247         interface_changed ();
248 }
249         
250 void
251 EngineControl::driver_changed ()
252 {
253         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
254         assert (backend);
255
256         backend->set_driver (driver_combo.get_active_text());
257         list_devices ();
258 }
259
260 void
261 EngineControl::interface_changed ()
262 {
263         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
264         assert (backend);
265         string device_name = interface_combo.get_active_text ();
266         vector<string> s;
267
268         /* don't allow programmatic change to sample_rate_combo to cause a
269            recursive call to this method.
270         */
271            
272         sr_connection.block ();
273
274         /* sample rates */
275
276         vector<float> sr = backend->available_sample_rates (device_name);
277         for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
278                 char buf[32];
279                 if (fmod (*x, 1000.0f)) {
280                         snprintf (buf, sizeof (buf), "%.1f kHz", (*x)/1000.0);
281                 } else {
282                         snprintf (buf, sizeof (buf), "%.0f kHz", (*x)/1000.0);
283                 }
284                 s.push_back (buf);
285         }
286
287         set_popdown_strings (sample_rate_combo, s);
288         sample_rate_combo.set_active_text (s.front());
289
290         reshow_buffer_sizes ();
291         
292         sr_connection.unblock ();
293 }       
294
295 void
296 EngineControl::reshow_buffer_sizes ()
297 {
298         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
299         assert (backend);
300         string device_name = interface_combo.get_active_text ();
301         vector<string> s;
302
303         /* buffer sizes */
304
305         s.clear ();
306         vector<uint32_t> bs = backend->available_buffer_sizes(device_name);
307         uint32_t rate = get_rate();
308
309         for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
310                 char buf[32];
311                 /* Translators: "samples" is ALWAYS plural here, so we do not
312                    need singular form as well. Same for msecs.
313                 */
314                 snprintf (buf, sizeof (buf), _("%u samples (%.1f msecs)"), *x, (2 * (*x)) / (rate/1000.0));
315                 s.push_back (buf);
316         }
317
318         set_popdown_strings (buffer_size_combo, s);
319         buffer_size_combo.set_active_text (s.front());
320 }
321
322 void
323 EngineControl::audio_mode_changed ()
324 {
325         std::string str = audio_mode_combo.get_active_text();
326
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);
339         }
340 }
341
342 XMLNode&
343 EngineControl::get_state ()
344 {
345         XMLNode* root = new XMLNode ("AudioSetup");
346         XMLNode* child;
347         std::string path;
348
349 #if 0
350         child = new XMLNode ("periods");
351         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
352         root->add_child_nocopy (*child);
353
354         child = new XMLNode ("ports");
355         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
356         root->add_child_nocopy (*child);
357
358         child = new XMLNode ("inlatency");
359         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
360         root->add_child_nocopy (*child);
361
362         child = new XMLNode ("outlatency");
363         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
364         root->add_child_nocopy (*child);
365
366         child = new XMLNode ("realtime");
367         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
368         root->add_child_nocopy (*child);
369
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);
373
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);
377
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);
381
382         child = new XMLNode ("force16bit");
383         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
384         root->add_child_nocopy (*child);
385
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);
389
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);
393
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);
397
398         child = new XMLNode ("samplerate");
399         child->add_property ("val", sample_rate_combo.get_active_text());
400         root->add_child_nocopy (*child);
401
402         child = new XMLNode ("periodsize");
403         child->add_property ("val", period_size_combo.get_active_text());
404         root->add_child_nocopy (*child);
405
406         child = new XMLNode ("serverpath");
407         child->add_property ("val", serverpath_combo.get_active_text());
408         root->add_child_nocopy (*child);
409
410         child = new XMLNode ("driver");
411         child->add_property ("val", driver_combo.get_active_text());
412         root->add_child_nocopy (*child);
413
414         child = new XMLNode ("interface");
415         child->add_property ("val", interface_combo.get_active_text());
416         root->add_child_nocopy (*child);
417
418         child = new XMLNode ("timeout");
419         child->add_property ("val", timeout_combo.get_active_text());
420         root->add_child_nocopy (*child);
421
422         child = new XMLNode ("dither");
423         child->add_property ("val", dither_mode_combo.get_active_text());
424         root->add_child_nocopy (*child);
425
426         child = new XMLNode ("audiomode");
427         child->add_property ("val", audio_mode_combo.get_active_text());
428         root->add_child_nocopy (*child);
429
430         child = new XMLNode ("inputdevice");
431         child->add_property ("val", input_device_combo.get_active_text());
432         root->add_child_nocopy (*child);
433
434         child = new XMLNode ("outputdevice");
435         child->add_property ("val", output_device_combo.get_active_text());
436         root->add_child_nocopy (*child);
437
438         child = new XMLNode ("mididriver");
439         child->add_property ("val", midi_driver_combo.get_active_text());
440         root->add_child_nocopy (*child);
441 #endif
442         return *root;
443 }
444
445 void
446 EngineControl::set_state (const XMLNode& root)
447 {
448 #if 0
449         XMLNodeList          clist;
450         XMLNodeConstIterator citer;
451         XMLNode* child;
452         XMLProperty* prop = NULL;
453         bool using_dummy = false;
454         bool using_ffado = false;
455
456         int val;
457         string strval;
458
459         if ( (child = root.child ("driver"))){
460                 prop = child->property("val");
461
462                 if (prop && (prop->value() == "Dummy") ) {
463                         using_dummy = true;
464                 }
465                 if (prop && (prop->value() == "FFADO") ) {
466                         using_ffado = true;
467                 }
468
469         }
470
471         clist = root.children();
472
473         for (citer = clist.begin(); citer != clist.end(); ++citer) {
474
475                 child = *citer;
476
477                 prop = child->property ("val");
478
479                 if (!prop || prop->value().empty()) {
480
481                         if (((using_dummy || using_ffado)
482                                 && ( child->name() == "interface"
483                                         || child->name() == "inputdevice"
484                                         || child->name() == "outputdevice"))
485                                 || child->name() == "timeout")
486                         {
487                                 continue;
488                         }
489
490                         error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
491                         continue;
492                 }
493
494                 strval = prop->value();
495
496                 /* adjustments/spinners */
497
498                 if (child->name() == "periods") {
499                         val = atoi (strval);
500                         periods_adjustment.set_value(val);
501                 } else if (child->name() == "ports") {
502                         val = atoi (strval);
503                         ports_adjustment.set_value(val);
504                 } else if (child->name() == "inlatency") {
505                         val = atoi (strval);
506                         input_latency.set_value(val);
507                 } else if (child->name() == "outlatency") {
508                         val = atoi (strval);
509                         output_latency.set_value(val);
510                 }
511
512                 /* buttons */
513
514                 else if (child->name() == "realtime") {
515                         val = atoi (strval);
516                         realtime_button.set_active(val);
517                 } else if (child->name() == "nomemorylock") {
518                         val = atoi (strval);
519                         no_memory_lock_button.set_active(val);
520                 } else if (child->name() == "unlockmemory") {
521                         val = atoi (strval);
522                         unlock_memory_button.set_active(val);
523                 } else if (child->name() == "softmode") {
524                         val = atoi (strval);
525                         soft_mode_button.set_active(val);
526                 } else if (child->name() == "force16bit") {
527                         val = atoi (strval);
528                         force16bit_button.set_active(val);
529                 } else if (child->name() == "hwmonitor") {
530                         val = atoi (strval);
531                         hw_monitor_button.set_active(val);
532                 } else if (child->name() == "hwmeter") {
533                         val = atoi (strval);
534                         hw_meter_button.set_active(val);
535                 } else if (child->name() == "verbose") {
536                         val = atoi (strval);
537                         verbose_output_button.set_active(val);
538                 }
539
540                 /* combos */
541
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") {
547
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 ...)
552                         */
553
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.
559                                 */
560                                 vector<string>::iterator x;
561                                 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
562                                         if (*x == strval) {
563                                                 break;
564                                         }
565                                 }
566                                 if (x != server_strings.end()) {
567                                         serverpath_combo.set_active_text (strval);
568                                 } else {
569                                         warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
570                                                                    strval)
571                                                 << endmsg;
572                                 }
573                         }
574
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);
591                 }
592         }
593 #endif
594 }
595
596 int
597 EngineControl::setup_engine (bool start)
598 {
599         boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
600         assert (backend);
601
602         /* grab the parameters from the GUI and apply them */
603
604         try {
605                 if (backend->requires_driver_selection()) {
606                         if (backend->set_driver (get_driver())) {
607                                 return -1;
608                         }
609                 }
610
611                 if (backend->set_device_name (get_device_name())) {
612                         return -1;
613                 }
614
615                 if (backend->set_sample_rate (get_rate())) {
616                         error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
617                         return -1;
618                 }
619                 if (backend->set_buffer_size (get_buffer_size())) {
620                         error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
621                         return -1;
622                 }
623                 if (backend->set_input_channels (get_input_channels())) {
624                         error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
625                         return -1;
626                 }
627                 if (backend->set_output_channels (get_output_channels())) {
628                         error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
629                         return -1;
630                 }
631                 if (backend->set_systemic_input_latency (get_input_latency())) {
632                         error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
633                         return -1;
634                 }
635                 if (backend->set_systemic_output_latency (get_output_latency())) {
636                         error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
637                         return -1;
638                 }
639
640                 if (start) {
641                         return ARDOUR::AudioEngine::instance()->start();
642                 }
643
644                 return 0;
645
646         } catch (...) {
647                 cerr << "exception thrown...\n";
648                 return -1;
649         }
650 }
651
652 uint32_t
653 EngineControl::get_rate () const
654 {
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.
658          */
659         if (r < 1000.0) {
660                 r *= 1000.0;
661         }
662         return lrint (r);
663 }
664
665 uint32_t
666 EngineControl::get_buffer_size () const
667 {
668         string txt = buffer_size_combo.get_active_text ();
669         uint32_t samples;
670
671         if (sscanf (txt.c_str(), "%d", &samples) != 1) {
672                 throw exception ();
673         }
674
675         return samples;
676 }
677
678 uint32_t
679 EngineControl::get_input_channels() const
680 {
681         return (uint32_t) input_channels_adjustment.get_value();
682 }
683
684 uint32_t
685 EngineControl::get_output_channels() const
686 {
687         return (uint32_t) output_channels_adjustment.get_value();
688 }
689
690 uint32_t
691 EngineControl::get_input_latency() const
692 {
693         return (uint32_t) input_latency_adjustment.get_value();
694 }
695
696 uint32_t
697 EngineControl::get_output_latency() const
698 {
699         return (uint32_t) output_latency_adjustment.get_value();
700 }
701
702 string
703 EngineControl::get_driver () const
704 {
705         return driver_combo.get_active_text ();
706 }
707
708 string
709 EngineControl::get_device_name () const
710 {
711         return interface_combo.get_active_text ();
712 }
713