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