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