Provide both FreeBoB and FFADO options in the engine dialogue. Fixes #3457.
[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 <vector>
21 #include <cmath>
22 #include <fstream>
23 #include <map>
24
25 #include <glibmm.h>
26 #include <gtkmm/messagedialog.h>
27 #include "pbd/xml++.h"
28
29 #ifdef __APPLE__
30 #include <CoreAudio/CoreAudio.h>
31 #include <CoreFoundation/CFString.h>
32 #include <sys/param.h>
33 #include <mach-o/dyld.h>
34 #else
35 #include <alsa/asoundlib.h>
36 #endif
37
38 #include "ardour/profile.h"
39 #include <jack/jack.h>
40
41 #include <gtkmm/stock.h>
42 #include <gtkmm2ext/utils.h>
43
44 #include "pbd/convert.h"
45 #include "pbd/error.h"
46 #include "pbd/pathscanner.h"
47
48 #ifdef __APPLE
49 #include <CFBundle.h>
50 #endif
51
52 #include "engine_dialog.h"
53 #include "i18n.h"
54
55 using namespace std;
56 using namespace Gtk;
57 using namespace Gtkmm2ext;
58 using namespace PBD;
59 using namespace Glib;
60
61 EngineControl::EngineControl ()
62         : periods_adjustment (2, 2, 16, 1, 2),
63           periods_spinner (periods_adjustment),
64           priority_adjustment (60, 10, 90, 1, 10),
65           priority_spinner (priority_adjustment),
66           ports_adjustment (128, 8, 1024, 1, 16),
67           ports_spinner (ports_adjustment),
68           input_latency_adjustment (0, 0, 99999, 1),
69           input_latency (input_latency_adjustment),
70           output_latency_adjustment (0, 0, 99999, 1),
71           output_latency (output_latency_adjustment),
72           realtime_button (_("Realtime")),
73           no_memory_lock_button (_("Do not lock memory")),
74           unlock_memory_button (_("Unlock memory")),
75           soft_mode_button (_("No zombies")),
76           monitor_button (_("Provide monitor ports")),
77           force16bit_button (_("Force 16 bit")),
78           hw_monitor_button (_("H/W monitoring")),
79           hw_meter_button (_("H/W metering")),
80           verbose_output_button (_("Verbose output")),
81           start_button (_("Start")),
82           stop_button (_("Stop")),
83 #ifdef __APPLE__
84           basic_packer (5, 2),
85           options_packer (4, 2),
86           device_packer (4, 2)
87 #else
88           basic_packer (8, 2),
89           options_packer (14, 2),
90           device_packer (6, 2)
91 #endif
92 {
93         using namespace Notebook_Helpers;
94         Label* label;
95         vector<string> strings;
96         int row = 0;
97
98         _used = false;
99
100         strings.push_back (_("8000Hz"));
101         strings.push_back (_("22050Hz"));
102         strings.push_back (_("44100Hz"));
103         strings.push_back (_("48000Hz"));
104         strings.push_back (_("88200Hz"));
105         strings.push_back (_("96000Hz"));
106         strings.push_back (_("192000Hz"));
107         set_popdown_strings (sample_rate_combo, strings);
108         sample_rate_combo.set_active_text ("48000Hz");
109
110         strings.clear ();
111         strings.push_back ("32");
112         strings.push_back ("64");
113         strings.push_back ("128");
114         strings.push_back ("256");
115         strings.push_back ("512");
116         strings.push_back ("1024");
117         strings.push_back ("2048");
118         strings.push_back ("4096");
119         strings.push_back ("8192");
120         set_popdown_strings (period_size_combo, strings);
121         period_size_combo.set_active_text ("1024");
122
123         strings.clear ();
124         strings.push_back (_("None"));
125         strings.push_back (_("Triangular"));
126         strings.push_back (_("Rectangular"));
127         strings.push_back (_("Shaped"));
128         set_popdown_strings (dither_mode_combo, strings);
129         dither_mode_combo.set_active_text (_("None"));
130
131         /* basic parameters */
132
133         basic_packer.set_spacings (6);
134
135         strings.clear ();
136 #ifdef __APPLE__
137         strings.push_back (X_("CoreAudio"));
138 #else
139         strings.push_back (X_("ALSA"));
140         strings.push_back (X_("OSS"));
141         strings.push_back (X_("FreeBoB"));
142         strings.push_back (X_("FFADO"));
143 #endif
144         strings.push_back (X_("NetJACK"));
145         strings.push_back (X_("Dummy"));
146         set_popdown_strings (driver_combo, strings);
147         driver_combo.set_active_text (strings.front());
148
149         driver_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::driver_changed));
150         driver_changed ();
151
152         strings.clear ();
153         strings.push_back (_("Playback/recording on 1 device"));
154         strings.push_back (_("Playback/recording on 2 devices"));
155         strings.push_back (_("Playback only"));
156         strings.push_back (_("Recording only"));
157         set_popdown_strings (audio_mode_combo, strings);
158         audio_mode_combo.set_active_text (strings.front());
159
160         audio_mode_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::audio_mode_changed));
161         audio_mode_changed ();
162
163         strings.clear ();
164         strings.push_back (_("None"));
165         strings.push_back (_("seq"));
166         strings.push_back (_("raw"));
167         set_popdown_strings (midi_driver_combo, strings);
168         midi_driver_combo.set_active_text (strings.front ());
169
170         row = 0;
171
172         label = manage (new Label (_("Driver:")));
173         label->set_alignment (0, 0.5);
174         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
175         basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
176         row++;
177
178         label = manage (new Label (_("Interface:")));
179         label->set_alignment (0, 0.5);
180         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
181         basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
182         row++;
183
184         label = manage (new Label (_("Sample rate:")));
185         label->set_alignment (0, 0.5);
186         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
187         basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
188         row++;
189
190         label = manage (new Label (_("Buffer size:")));
191         label->set_alignment (0, 0.5);
192         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
193         basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
194         row++;
195
196 #ifndef __APPLE__
197         label = manage (new Label (_("Number of buffers:")));
198         label->set_alignment (0, 0.5);
199         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
200         basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
201         periods_spinner.set_value (2);
202         row++;
203 #endif
204
205         label = manage (new Label (_("Approximate latency:")));
206         label->set_alignment (0, 0.5);
207         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
208         basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
209         row++;
210
211         sample_rate_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
212         periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
213         period_size_combo.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
214         redisplay_latency();
215         row++;
216         /* no audio mode with CoreAudio, its duplex or nuthin' */
217
218 #ifndef __APPLE__
219         label = manage (new Label (_("Audio mode:")));
220         label->set_alignment (0, 0.5);
221         basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
222         basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
223         row++;
224 #endif
225
226         interface_combo.set_size_request (250, -1);
227         input_device_combo.set_size_request (250, -1);
228         output_device_combo.set_size_request (250, -1);
229
230         /*
231
232         if (engine_running()) {
233                 start_button.set_sensitive (false);
234         } else {
235                 stop_button.set_sensitive (false);
236         }
237
238         start_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
239         stop_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::start_engine));
240         */
241
242         button_box.pack_start (start_button, false, false);
243         button_box.pack_start (stop_button, false, false);
244
245         // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
246
247         /* options */
248
249         options_packer.set_spacings (6);
250         row = 0;
251
252         options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
253         ++row;
254
255         realtime_button.set_active (true);
256         realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
257         realtime_changed ();
258
259 #if PROVIDE_TOO_MANY_OPTIONS
260
261 #ifndef __APPLE__
262         label = manage (new Label (_("Realtime Priority")));
263         label->set_alignment (1.0, 0.5);
264         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
265         options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266         ++row;
267         priority_spinner.set_value (60);
268
269         options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
270         ++row;
271         options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
272         ++row;
273         options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
274         ++row;
275         options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276         ++row;
277         options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278         ++row;
279         options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280         ++row;
281         options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
282         ++row;
283         options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
284         ++row;
285 #else
286         options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
287         ++row;
288 #endif
289
290         strings.clear ();
291         strings.push_back (_("Ignore"));
292         strings.push_back ("500 msec");
293         strings.push_back ("1 sec");
294         strings.push_back ("2 sec");
295         strings.push_back ("10 sec");
296         set_popdown_strings (timeout_combo, strings);
297         timeout_combo.set_active_text (strings.front ());
298
299         label = manage (new Label (_("Client timeout")));
300         label->set_alignment (1.0, 0.5);
301         options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
302         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
303         ++row;
304
305 #endif /* PROVIDE_TOO_MANY_OPTIONS */
306         label = manage (new Label (_("Number of ports:")));
307         label->set_alignment (0, 0.5);
308         options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
309         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
310         ++row;
311
312         label = manage (new Label (_("MIDI driver:")));
313         label->set_alignment (0, 0.5);
314         options_packer.attach (midi_driver_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
315         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
316         ++row;
317
318 #ifndef __APPLE__
319         label = manage (new Label (_("Dither:")));
320         label->set_alignment (0, 0.5);
321         options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
322         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
323         ++row;
324 #endif
325
326         find_jack_servers (server_strings);
327
328         if (server_strings.empty()) {
329                 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
330                 /*NOTREACHED*/
331         }
332
333         set_popdown_strings (serverpath_combo, server_strings);
334         serverpath_combo.set_active_text (server_strings.front());
335
336         if (server_strings.size() > 1) {
337                 label = manage (new Label (_("Server:")));
338                 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
339                 label->set_alignment (0.0, 0.5);
340                 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
341                 ++row;
342         }
343
344         /* device settings */
345
346         device_packer.set_spacings (6);
347         row = 0;
348
349 #ifndef __APPLE__
350         label = manage (new Label (_("Input device:")));
351         label->set_alignment (0, 0.5);
352         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
353         device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
354         ++row;
355         label = manage (new Label (_("Output device:")));
356         label->set_alignment (0, 0.5);
357         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
358         device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
359         ++row;
360 #endif
361         label = manage (new Label (_("Input channels:")));
362         label->set_alignment (0, 0.5);
363         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
364         device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
365         ++row;
366         label = manage (new Label (_("Output channels:")));
367         label->set_alignment (0, 0.5);
368         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
369         device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
370         ++row;
371         label = manage (new Label (_("Hardware input latency:")));
372         label->set_alignment (0, 0.5);
373         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
374         device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
375         label = manage (new Label (_("samples")));
376         label->set_alignment (0, 0.5);
377         device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
378         ++row;
379         label = manage (new Label (_("Hardware output latency:")));
380         label->set_alignment (0, 0.5);
381         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
382         device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
383         label = manage (new Label (_("samples")));
384         label->set_alignment (0, 0.5);
385         device_packer.attach (*label, 2, 3, row, row+1, FILL|EXPAND, (AttachOptions) 0);
386         ++row;
387
388         basic_hbox.pack_start (basic_packer, false, false);
389         options_hbox.pack_start (options_packer, false, false);
390
391         device_packer.set_border_width (12);
392         options_packer.set_border_width (12);
393         basic_packer.set_border_width (12);
394
395         notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
396         notebook.pages().push_back (TabElem (options_hbox, _("Options")));
397         notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
398         notebook.set_border_width (12);
399
400         set_border_width (12);
401         pack_start (notebook);
402 }
403
404 EngineControl::~EngineControl ()
405 {
406
407 }
408
409 void
410 EngineControl::build_command_line (vector<string>& cmd)
411 {
412         string str;
413         string driver;
414         bool using_alsa = false;
415         bool using_coreaudio = false;
416         bool using_dummy = false;
417
418         /* first, path to jackd */
419
420         cmd.push_back (serverpath_combo.get_active_text ());
421
422         /* now jackd arguments */
423
424         str = timeout_combo.get_active_text ();
425         if (str != _("Ignore")) {
426                 double secs = 0;
427                 uint32_t msecs;
428                 secs = atof (str);
429                 msecs = (uint32_t) floor (secs * 1000.0);
430                 if (msecs > 0) {
431                         cmd.push_back ("-t");
432                         cmd.push_back (to_string (msecs, std::dec));
433                 }
434         }
435
436         if (no_memory_lock_button.get_active()) {
437                 cmd.push_back ("-m"); /* no munlock */
438         }
439
440         cmd.push_back ("-p"); /* port max */
441         cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
442
443         if (realtime_button.get_active()) {
444                 cmd.push_back ("-R");
445                 cmd.push_back ("-P");
446                 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
447         } else {
448                 cmd.push_back ("-r"); /* override jackd's default --realtime */
449         }
450
451         if (unlock_memory_button.get_active()) {
452                 cmd.push_back ("-u");
453         }
454
455         if (verbose_output_button.get_active()) {
456                 cmd.push_back ("-v");
457         }
458
459         /* now add fixed arguments (not user-selectable) */
460
461         cmd.push_back ("-T"); // temporary */
462
463         /* next the driver */
464
465         cmd.push_back ("-d");
466
467         driver = driver_combo.get_active_text ();
468         if (driver == X_("ALSA")) {
469                 using_alsa = true;
470                 cmd.push_back ("alsa");
471         } else if (driver == X_("OSS")) {
472                 cmd.push_back ("oss");
473         } else if (driver == X_("CoreAudio")) {
474                 using_coreaudio = true;
475                 cmd.push_back ("coreaudio");
476         } else if (driver == X_("NetJACK")) {
477                 cmd.push_back ("netjack");
478         } else if (driver == X_("FreeBoB")) {
479                 cmd.push_back ("freebob");
480         } else if (driver == X_("FFADO")) {
481                 cmd.push_back ("firewire");
482         } else if ( driver == X_("Dummy")) {
483                 using_dummy = true;
484                 cmd.push_back ("dummy");
485         }
486
487         /* driver arguments */
488
489         if (!using_coreaudio) {
490                 str = audio_mode_combo.get_active_text();
491
492                 if (str == _("Playback/Recording on 1 Device")) {
493
494                         /* relax */
495
496                 } else if (str == _("Playback/Recording on 2 Devices")) {
497
498                         string input_device = get_device_name (driver, input_device_combo.get_active_text());
499                         string output_device = get_device_name (driver, output_device_combo.get_active_text());
500
501                         if (input_device.empty() || output_device.empty()) {
502                                 cmd.clear ();
503                                 return;
504                         }
505
506                         cmd.push_back ("-C");
507                         cmd.push_back (input_device);
508                         cmd.push_back ("-P");
509                         cmd.push_back (output_device);
510
511                 } else if (str == _("Playback only")) {
512                         cmd.push_back ("-P");
513                 } else if (str == _("Recording only")) {
514                         cmd.push_back ("-C");
515                 }
516
517                 if (!using_dummy) {
518                         cmd.push_back ("-n");
519                         cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
520                 }
521         }
522
523         cmd.push_back ("-r");
524         cmd.push_back (to_string (get_rate(), std::dec));
525
526         cmd.push_back ("-p");
527         cmd.push_back (period_size_combo.get_active_text());
528
529         if (using_alsa) {
530
531                 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
532
533                         string device = get_device_name (driver, interface_combo.get_active_text());
534                         if (device.empty()) {
535                                 cmd.clear ();
536                                 return;
537                         }
538
539                         cmd.push_back ("-d");
540                         cmd.push_back (device);
541                 }
542
543                 if (hw_meter_button.get_active()) {
544                         cmd.push_back ("-M");
545                 }
546
547                 if (hw_monitor_button.get_active()) {
548                         cmd.push_back ("-H");
549                 }
550
551                 str = dither_mode_combo.get_active_text();
552
553                 if (str == _("None")) {
554                 } else if (str == _("Triangular")) {
555                         cmd.push_back ("-z triangular");
556                 } else if (str == _("Rectangular")) {
557                         cmd.push_back ("-z rectangular");
558                 } else if (str == _("Shaped")) {
559                         cmd.push_back ("-z shaped");
560                 }
561
562                 if (force16bit_button.get_active()) {
563                         cmd.push_back ("-S");
564                 }
565
566                 if (soft_mode_button.get_active()) {
567                         cmd.push_back ("-s");
568                 }
569
570                 str = midi_driver_combo.get_active_text ();
571
572                 if (str == _("seq")) {
573                         cmd.push_back ("-X seq");
574                 } else if (str == _("raw")) {
575                         cmd.push_back ("-X raw");
576                 }
577
578                 double val = input_latency_adjustment.get_value();
579
580                 if (val) {
581                         cmd.push_back ("-I");
582                         cmd.push_back (to_string ((uint32_t) val, std::dec));
583                 }
584
585                 val = output_latency_adjustment.get_value();
586                 if (val) {
587                         cmd.push_back ("-O");
588                         cmd.push_back (to_string ((uint32_t) val, std::dec));
589                 }
590
591         } else if (using_coreaudio) {
592
593 #ifdef __APPLE__
594                 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
595
596                 string device = get_device_name (driver, interface_combo.get_active_text());
597                 if (device.empty()) {
598                         cmd.clear ();
599                         return;
600                 }
601
602                 cmd.push_back ("-d");
603                 cmd.push_back (device);
604
605                 double val = input_latency_adjustment.get_value();
606
607                 if (val) {
608                         cmd.push_back ("-I");
609                         cmd.push_back (to_string ((uint32_t) val, std::dec));
610                 }
611
612                 double val = output_latency_adjustment.get_value();
613                 if (val) {
614                         cmd.push_back ("-O");
615                         cmd.push_back (to_string ((uint32_t) val, std::dec));
616                 }
617 #endif
618
619         }
620 }
621
622 bool
623 EngineControl::engine_running ()
624 {
625         jack_status_t status;
626         jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
627
628         if (status == 0) {
629                 jack_client_close (c);
630                 return true;
631         }
632         return false;
633 }
634
635 int
636 EngineControl::setup_engine ()
637 {
638         vector<string> args;
639         std::string cwd = "/tmp";
640
641         build_command_line (args);
642
643         if (args.empty()) {
644                 return 1; // try again
645         }
646
647         std::string jackdrc_path = Glib::get_home_dir();
648         jackdrc_path += "/.jackdrc";
649
650         ofstream jackdrc (jackdrc_path.c_str());
651         if (!jackdrc) {
652                 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
653                 return -1;
654         }
655         cerr << "JACK COMMAND: ";
656         for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
657                 cerr << (*i) << ' ';
658                 jackdrc << (*i) << ' ';
659         }
660         cerr << endl;
661         jackdrc << endl;
662         jackdrc.close ();
663
664         _used = true;
665
666         return 0;
667 }
668
669 void
670 EngineControl::realtime_changed ()
671 {
672 #ifndef __APPLE__
673         priority_spinner.set_sensitive (realtime_button.get_active());
674 #endif
675 }
676
677 void
678 EngineControl::enumerate_devices (const string& driver)
679 {
680         /* note: case matters for the map keys */
681
682         if (driver == "CoreAudio") {
683 #ifdef __APPLE__
684                 devices[driver] = enumerate_coreaudio_devices ();
685 #endif
686
687 #ifndef __APPLE__
688         } else if (driver == "ALSA") {
689                 devices[driver] = enumerate_alsa_devices ();
690         } else if (driver == "FreeBOB") {
691                 devices[driver] = enumerate_freebob_devices ();
692         } else if (driver == "FFADO") {
693                 devices[driver] = enumerate_ffado_devices ();
694         } else if (driver == "OSS") {
695                 devices[driver] = enumerate_oss_devices ();
696         } else if (driver == "Dummy") {
697                 devices[driver] = enumerate_dummy_devices ();
698         } else if (driver == "NetJACK") {
699                 devices[driver] = enumerate_netjack_devices ();
700         }
701 #else
702         }
703 #endif
704 }
705
706 #ifdef __APPLE__
707 static OSStatus
708 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
709 {
710         UInt32 size = sizeof(CFStringRef);
711         CFStringRef UI;
712         OSStatus res = AudioDeviceGetProperty(id, 0, false,
713                 kAudioDevicePropertyDeviceUID, &size, &UI);
714         if (res == noErr)
715                 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
716         CFRelease(UI);
717         return res;
718 }
719
720 vector<string>
721 EngineControl::enumerate_coreaudio_devices ()
722 {
723         vector<string> devs;
724
725         // Find out how many Core Audio devices are there, if any...
726         // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
727         OSStatus err;
728         Boolean isWritable;
729         size_t outSize = sizeof(isWritable);
730
731         backend_devs.clear ();
732
733         err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
734                                            &outSize, &isWritable);
735         if (err == noErr) {
736                 // Calculate the number of device available...
737                 int numCoreDevices = outSize / sizeof(AudioDeviceID);
738                 // Make space for the devices we are about to get...
739                 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
740                 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
741                                                &outSize, (void *) coreDeviceIDs);
742                 if (err == noErr) {
743                         // Look for the CoreAudio device name...
744                         char coreDeviceName[256];
745                         size_t nameSize;
746
747                         for (int i = 0; i < numCoreDevices; i++) {
748
749                                 nameSize = sizeof (coreDeviceName);
750
751                                 /* enforce duplex devices only */
752
753                                 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
754                                                                  0, true, kAudioDevicePropertyStreams,
755                                                                  &outSize, &isWritable);
756
757                                 if (err != noErr || outSize == 0) {
758                                         continue;
759                                 }
760
761                                 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
762                                                                  0, false, kAudioDevicePropertyStreams,
763                                                                  &outSize, &isWritable);
764
765                                 if (err != noErr || outSize == 0) {
766                                         continue;
767                                 }
768
769                                 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
770                                                                  0, true, kAudioDevicePropertyDeviceName,
771                                                                  &outSize, &isWritable);
772                                 if (err == noErr) {
773                                         err = AudioDeviceGetProperty(coreDeviceIDs[i],
774                                                                      0, true, kAudioDevicePropertyDeviceName,
775                                                                      &nameSize, (void *) coreDeviceName);
776                                         if (err == noErr) {
777                                                 char drivername[128];
778
779                                                 // this returns the unique id for the device
780                                                 // that must be used on the commandline for jack
781
782                                                 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
783                                                         devs.push_back (coreDeviceName);
784                                                         backend_devs.push_back (drivername);
785                                                 }
786                                         }
787                                 }
788                         }
789                 }
790                 delete [] coreDeviceIDs;
791         }
792
793
794         if (devs.size() == 0) {
795                 MessageDialog msg (_("\
796 You do not have any audio devices capable of\n\
797 simultaneous playback and recording.\n\n\
798 Please use Applications -> Utilities -> Audio MIDI Setup\n\
799 to create an \"aggregrate\" device, or install a suitable\n\
800 audio interface.\n\n\
801 Please send email to Apple and ask them why new Macs\n\
802 have no duplex audio device.\n\n\
803 Alternatively, if you really want just playback\n\
804 or recording but not both, start JACK before running\n\
805 Ardour and choose the relevant device then."
806                                            ),
807                                    true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
808                 msg.set_title (_("No suitable audio devices"));
809                 msg.set_position (Gtk::WIN_POS_MOUSE);
810                 msg.run ();
811                 exit (1);
812         }
813
814
815         return devs;
816 }
817 #else
818 vector<string>
819 EngineControl::enumerate_alsa_devices ()
820 {
821         vector<string> devs;
822
823         snd_ctl_t *handle;
824         snd_ctl_card_info_t *info;
825         snd_pcm_info_t *pcminfo;
826         snd_ctl_card_info_alloca(&info);
827         snd_pcm_info_alloca(&pcminfo);
828         string devname;
829         int cardnum = -1;
830         int device = -1;
831
832         backend_devs.clear ();
833
834         while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
835
836                 devname = "hw:";
837                 devname += to_string (cardnum, std::dec);
838
839                 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
840
841                         while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
842
843                                 snd_pcm_info_set_device (pcminfo, device);
844                                 snd_pcm_info_set_subdevice (pcminfo, 0);
845                                 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
846
847                                 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
848                                         devs.push_back (snd_pcm_info_get_name (pcminfo));
849                                         devname += ',';
850                                         devname += to_string (device, std::dec);
851                                         backend_devs.push_back (devname);
852                                 }
853                         }
854
855                         snd_ctl_close(handle);
856                 }
857         }
858
859         return devs;
860 }
861
862 vector<string>
863 EngineControl::enumerate_ffado_devices ()
864 {
865         vector<string> devs;
866         backend_devs.clear ();
867         return devs;
868 }
869
870 vector<string>
871 EngineControl::enumerate_freebob_devices ()
872 {
873         vector<string> devs;
874         return devs;
875 }
876
877 vector<string>
878 EngineControl::enumerate_oss_devices ()
879 {
880         vector<string> devs;
881         return devs;
882 }
883 vector<string>
884 EngineControl::enumerate_dummy_devices ()
885 {
886         vector<string> devs;
887         return devs;
888 }
889 vector<string>
890 EngineControl::enumerate_netjack_devices ()
891 {
892         vector<string> devs;
893         return devs;
894 }
895 #endif
896
897 void
898 EngineControl::driver_changed ()
899 {
900         string driver = driver_combo.get_active_text();
901         string::size_type maxlen = 0;
902         int maxindex = -1;
903         int n = 0;
904
905         enumerate_devices (driver);
906
907         vector<string>& strings = devices[driver];
908
909         if (strings.empty() && driver != "FreeBoB" && driver != "FFADO" && driver != "Dummy") {
910                 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
911                 return;
912         }
913
914         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
915                 if ((*i).length() > maxlen) {
916                         maxlen = (*i).length();
917                         maxindex = n;
918                 }
919         }
920
921         set_popdown_strings (interface_combo, strings);
922         set_popdown_strings (input_device_combo, strings);
923         set_popdown_strings (output_device_combo, strings);
924
925         if (!strings.empty()) {
926                 interface_combo.set_active_text (strings.front());
927                 input_device_combo.set_active_text (strings.front());
928                 output_device_combo.set_active_text (strings.front());
929         }
930
931         if (driver == "ALSA") {
932                 soft_mode_button.set_sensitive (true);
933                 force16bit_button.set_sensitive (true);
934                 hw_monitor_button.set_sensitive (true);
935                 hw_meter_button.set_sensitive (true);
936                 monitor_button.set_sensitive (true);
937         } else {
938                 soft_mode_button.set_sensitive (false);
939                 force16bit_button.set_sensitive (false);
940                 hw_monitor_button.set_sensitive (false);
941                 hw_meter_button.set_sensitive (false);
942                 monitor_button.set_sensitive (false);
943         }
944 }
945
946 uint32_t
947 EngineControl::get_rate ()
948 {
949         return atoi (sample_rate_combo.get_active_text ());
950 }
951
952 void
953 EngineControl::redisplay_latency ()
954 {
955         uint32_t rate = get_rate();
956 #ifdef __APPLE_
957         float periods = 2;
958 #else
959         float periods = periods_adjustment.get_value();
960 #endif
961         float period_size = atof (period_size_combo.get_active_text());
962
963         char buf[32];
964         snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
965
966         latency_label.set_text (buf);
967         latency_label.set_alignment (0, 0.5);
968 }
969
970 void
971 EngineControl::audio_mode_changed ()
972 {
973         std::string str = audio_mode_combo.get_active_text();
974
975         if (str == _("Playback/Recording on 1 Device")) {
976                 input_device_combo.set_sensitive (false);
977                 output_device_combo.set_sensitive (false);
978         } else if (str == _("Playback/Recording on 2 Devices")) {
979                 input_device_combo.set_sensitive (true);
980                 output_device_combo.set_sensitive (true);
981         } else if (str == _("Playback only")) {
982                 output_device_combo.set_sensitive (true);
983         } else if (str == _("Recording only")) {
984                 input_device_combo.set_sensitive (true);
985         }
986 }
987
988 static bool jack_server_filter(const string& str, void */*arg*/)
989 {
990    return str == "jackd" || str == "jackdmp";
991 }
992
993 void
994 EngineControl::find_jack_servers (vector<string>& strings)
995 {
996 #ifdef __APPLE__
997         /* this magic lets us finds the path to the OSX bundle, and then
998            we infer JACK's location from there
999         */
1000
1001         char execpath[MAXPATHLEN+1];
1002         uint32_t pathsz = sizeof (execpath);
1003
1004         _NSGetExecutablePath (execpath, &pathsz);
1005
1006         string path (Glib::path_get_dirname (execpath));
1007         path += "/jackd";
1008
1009         if (Glib::file_test (path, FILE_TEST_EXISTS)) {
1010                 strings.push_back (path);
1011         }
1012
1013         if (getenv ("ARDOUR_WITH_JACK")) {
1014                 /* no other options - only use the JACK we supply */
1015                 if (strings.empty()) {
1016                         fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
1017                         /*NOTREACHED*/
1018                 }
1019                 return;
1020         }
1021 #else
1022         string path;
1023 #endif
1024
1025         PathScanner scanner;
1026         vector<string *> *jack_servers;
1027         std::map<string,int> un;
1028         char *p;
1029         bool need_minimal_path = false;
1030
1031         p = getenv ("PATH");
1032
1033         if (p && *p) {
1034                 path = p;
1035         } else {
1036                 need_minimal_path = true;
1037         }
1038
1039 #ifdef __APPLE__
1040         // many mac users don't have PATH set up to include
1041         // likely installed locations of JACK
1042         need_minimal_path = true;
1043 #endif
1044
1045         if (need_minimal_path) {
1046                 if (path.empty()) {
1047                         path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
1048                 } else {
1049                         path += ":/usr/local/bin:/opt/local/bin";
1050                 }
1051         }
1052
1053 #ifdef __APPLE__
1054         // push it back into the environment so that auto-started JACK can find it.
1055         // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1056         setenv ("PATH", path.c_str(), 1);
1057 #endif
1058
1059         jack_servers = scanner (path, jack_server_filter, 0, false, true);
1060
1061         vector<string *>::iterator iter;
1062
1063         for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1064                 string p = **iter;
1065
1066                 if (un[p]++ == 0) {
1067                         strings.push_back(p);
1068                 }
1069         }
1070 }
1071
1072
1073 string
1074 EngineControl::get_device_name (const string& driver, const string& human_readable)
1075 {
1076         vector<string>::iterator n;
1077         vector<string>::iterator i;
1078
1079         if (human_readable.empty()) {
1080                 /* this can happen if the user's .ardourrc file has a device name from
1081                    another computer system in it
1082                 */
1083                 MessageDialog msg (_("You need to choose an audio device first."));
1084                 msg.run ();
1085                 return string();
1086         }
1087
1088         if (backend_devs.empty()) {
1089                 return human_readable;
1090         }
1091
1092         for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1093                 if (human_readable == (*i)) {
1094                         return (*n);
1095                 }
1096         }
1097
1098         if (i == devices[driver].end()) {
1099                 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1100         }
1101
1102         return string();
1103 }
1104
1105 XMLNode&
1106 EngineControl::get_state ()
1107 {
1108         XMLNode* root = new XMLNode ("AudioSetup");
1109         XMLNode* child;
1110         std::string path;
1111
1112         child = new XMLNode ("periods");
1113         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1114         root->add_child_nocopy (*child);
1115
1116         child = new XMLNode ("priority");
1117         child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1118         root->add_child_nocopy (*child);
1119
1120         child = new XMLNode ("ports");
1121         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1122         root->add_child_nocopy (*child);
1123
1124         child = new XMLNode ("inchannels");
1125         child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1126         root->add_child_nocopy (*child);
1127
1128         child = new XMLNode ("outchannels");
1129         child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1130         root->add_child_nocopy (*child);
1131
1132         child = new XMLNode ("inlatency");
1133         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1134         root->add_child_nocopy (*child);
1135
1136         child = new XMLNode ("outlatency");
1137         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1138         root->add_child_nocopy (*child);
1139
1140         child = new XMLNode ("realtime");
1141         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1142         root->add_child_nocopy (*child);
1143
1144         child = new XMLNode ("nomemorylock");
1145         child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1146         root->add_child_nocopy (*child);
1147
1148         child = new XMLNode ("unlockmemory");
1149         child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1150         root->add_child_nocopy (*child);
1151
1152         child = new XMLNode ("softmode");
1153         child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1154         root->add_child_nocopy (*child);
1155
1156         child = new XMLNode ("force16bit");
1157         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1158         root->add_child_nocopy (*child);
1159
1160         child = new XMLNode ("hwmonitor");
1161         child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1162         root->add_child_nocopy (*child);
1163
1164         child = new XMLNode ("hwmeter");
1165         child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1166         root->add_child_nocopy (*child);
1167
1168         child = new XMLNode ("verbose");
1169         child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1170         root->add_child_nocopy (*child);
1171
1172         child = new XMLNode ("samplerate");
1173         child->add_property ("val", sample_rate_combo.get_active_text());
1174         root->add_child_nocopy (*child);
1175
1176         child = new XMLNode ("periodsize");
1177         child->add_property ("val", period_size_combo.get_active_text());
1178         root->add_child_nocopy (*child);
1179
1180         child = new XMLNode ("serverpath");
1181         child->add_property ("val", serverpath_combo.get_active_text());
1182         root->add_child_nocopy (*child);
1183
1184         child = new XMLNode ("driver");
1185         child->add_property ("val", driver_combo.get_active_text());
1186         root->add_child_nocopy (*child);
1187
1188         child = new XMLNode ("interface");
1189         child->add_property ("val", interface_combo.get_active_text());
1190         root->add_child_nocopy (*child);
1191
1192         child = new XMLNode ("timeout");
1193         child->add_property ("val", timeout_combo.get_active_text());
1194         root->add_child_nocopy (*child);
1195
1196         child = new XMLNode ("dither");
1197         child->add_property ("val", dither_mode_combo.get_active_text());
1198         root->add_child_nocopy (*child);
1199
1200         child = new XMLNode ("audiomode");
1201         child->add_property ("val", audio_mode_combo.get_active_text());
1202         root->add_child_nocopy (*child);
1203
1204         child = new XMLNode ("inputdevice");
1205         child->add_property ("val", input_device_combo.get_active_text());
1206         root->add_child_nocopy (*child);
1207
1208         child = new XMLNode ("outputdevice");
1209         child->add_property ("val", output_device_combo.get_active_text());
1210         root->add_child_nocopy (*child);
1211
1212         child = new XMLNode ("mididriver");
1213         child->add_property ("val", midi_driver_combo.get_active_text());
1214         root->add_child_nocopy (*child);
1215
1216         return *root;
1217 }
1218
1219 void
1220 EngineControl::set_state (const XMLNode& root)
1221 {
1222         XMLNodeList          clist;
1223         XMLNodeConstIterator citer;
1224         XMLNode* child;
1225         XMLProperty* prop = NULL;
1226         bool using_dummy = false;
1227
1228         int val;
1229         string strval;
1230
1231         if ( (child = root.child ("driver"))){
1232                 prop = child->property("val");
1233                 if (prop && (prop->value() == "Dummy") ) {
1234                         using_dummy = true;
1235                 }
1236         }
1237
1238         clist = root.children();
1239
1240         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1241
1242                 child = *citer;
1243
1244                 prop = child->property ("val");
1245
1246                 if (!prop || prop->value().empty()) {
1247
1248                         if ((using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) ||
1249                                 child->name() == "timeout")
1250                                 continue;
1251                         error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1252                         continue;
1253                 }
1254
1255                 strval = prop->value();
1256
1257                 /* adjustments/spinners */
1258
1259                 if (child->name() == "periods") {
1260                         val = atoi (strval);
1261                         periods_adjustment.set_value(val);
1262                 } else if (child->name() == "priority") {
1263                         val = atoi (strval);
1264                         priority_adjustment.set_value(val);
1265                 } else if (child->name() == "ports") {
1266                         val = atoi (strval);
1267                         ports_adjustment.set_value(val);
1268                 } else if (child->name() == "inchannels") {
1269                         val = atoi (strval);
1270                         input_channels.set_value(val);
1271                 } else if (child->name() == "outchannels") {
1272                         val = atoi (strval);
1273                         output_channels.set_value(val);
1274                 } else if (child->name() == "inlatency") {
1275                         val = atoi (strval);
1276                         input_latency.set_value(val);
1277                 } else if (child->name() == "outlatency") {
1278                         val = atoi (strval);
1279                         output_latency.set_value(val);
1280                 }
1281
1282                 /* buttons */
1283
1284                 else if (child->name() == "realtime") {
1285                         val = atoi (strval);
1286                         realtime_button.set_active(val);
1287                 } else if (child->name() == "nomemorylock") {
1288                         val = atoi (strval);
1289                         no_memory_lock_button.set_active(val);
1290                 } else if (child->name() == "unlockmemory") {
1291                         val = atoi (strval);
1292                         unlock_memory_button.set_active(val);
1293                 } else if (child->name() == "softmode") {
1294                         val = atoi (strval);
1295                         soft_mode_button.set_active(val);
1296                 } else if (child->name() == "force16bit") {
1297                         val = atoi (strval);
1298                         force16bit_button.set_active(val);
1299                 } else if (child->name() == "hwmonitor") {
1300                         val = atoi (strval);
1301                         hw_monitor_button.set_active(val);
1302                 } else if (child->name() == "hwmeter") {
1303                         val = atoi (strval);
1304                         hw_meter_button.set_active(val);
1305                 } else if (child->name() == "verbose") {
1306                         val = atoi (strval);
1307                         verbose_output_button.set_active(val);
1308                 }
1309
1310                 /* combos */
1311
1312                 else if (child->name() == "samplerate") {
1313                         sample_rate_combo.set_active_text(strval);
1314                 } else if (child->name() == "periodsize") {
1315                         period_size_combo.set_active_text(strval);
1316                 } else if (child->name() == "serverpath") {
1317                         
1318                         /* only attempt to set this if we have bothered to look
1319                            up server names already. otherwise this is all
1320                            redundant (actually, all of this dialog/widget
1321                            is redundant in that case ...)
1322                         */
1323                         
1324                         if (!server_strings.empty()) {
1325                                 /* do not allow us to use a server path that doesn't
1326                                    exist on this system. this handles cases where
1327                                    the user has an RC file listing a serverpath
1328                                    from some other machine.
1329                                 */
1330                                 vector<string>::iterator x;
1331                                 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1332                                         if (*x == strval) {
1333                                                 break;
1334                                         }
1335                                 }
1336                                 if (x != server_strings.end()) {
1337                                         serverpath_combo.set_active_text (strval);
1338                                 } else {
1339                                         warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1340                                                                    strval)
1341                                                 << endmsg;
1342                                 }
1343                         }
1344                         
1345                 } else if (child->name() == "driver") {
1346                         driver_combo.set_active_text(strval);
1347                 } else if (child->name() == "interface") {
1348                         interface_combo.set_active_text(strval);
1349                 } else if (child->name() == "timeout") {
1350                         timeout_combo.set_active_text(strval);
1351                 } else if (child->name() == "dither") {
1352                         dither_mode_combo.set_active_text(strval);
1353                 } else if (child->name() == "audiomode") {
1354                         audio_mode_combo.set_active_text(strval);
1355                 } else if (child->name() == "inputdevice") {
1356                         input_device_combo.set_active_text(strval);
1357                 } else if (child->name() == "outputdevice") {
1358                         output_device_combo.set_active_text(strval);
1359                 } else if (child->name() == "mididriver") {
1360                         midi_driver_combo.set_active_text(strval);
1361                 }
1362         }
1363 }