moron
[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         if (!jack_servers) {
1069                 return;
1070         }
1071
1072         vector<string *>::iterator iter;
1073
1074         for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1075                 string p = **iter;
1076
1077                 if (un[p]++ == 0) {
1078                         strings.push_back(p);
1079                 }
1080         }
1081 }
1082
1083
1084 string
1085 EngineControl::get_device_name (const string& driver, const string& human_readable)
1086 {
1087         vector<string>::iterator n;
1088         vector<string>::iterator i;
1089
1090         if (human_readable.empty()) {
1091                 /* this can happen if the user's .ardourrc file has a device name from
1092                    another computer system in it
1093                 */
1094                 MessageDialog msg (_("You need to choose an audio device first."));
1095                 msg.run ();
1096                 return string();
1097         }
1098
1099         if (backend_devs.empty()) {
1100                 return human_readable;
1101         }
1102
1103         for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1104                 if (human_readable == (*i)) {
1105                         return (*n);
1106                 }
1107         }
1108
1109         if (i == devices[driver].end()) {
1110                 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1111         }
1112
1113         return string();
1114 }
1115
1116 XMLNode&
1117 EngineControl::get_state ()
1118 {
1119         XMLNode* root = new XMLNode ("AudioSetup");
1120         XMLNode* child;
1121         std::string path;
1122
1123         child = new XMLNode ("periods");
1124         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1125         root->add_child_nocopy (*child);
1126
1127         child = new XMLNode ("priority");
1128         child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1129         root->add_child_nocopy (*child);
1130
1131         child = new XMLNode ("ports");
1132         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1133         root->add_child_nocopy (*child);
1134
1135         child = new XMLNode ("inlatency");
1136         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1137         root->add_child_nocopy (*child);
1138
1139         child = new XMLNode ("outlatency");
1140         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1141         root->add_child_nocopy (*child);
1142
1143         child = new XMLNode ("realtime");
1144         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1145         root->add_child_nocopy (*child);
1146
1147         child = new XMLNode ("nomemorylock");
1148         child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1149         root->add_child_nocopy (*child);
1150
1151         child = new XMLNode ("unlockmemory");
1152         child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1153         root->add_child_nocopy (*child);
1154
1155         child = new XMLNode ("softmode");
1156         child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1157         root->add_child_nocopy (*child);
1158
1159         child = new XMLNode ("force16bit");
1160         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1161         root->add_child_nocopy (*child);
1162
1163         child = new XMLNode ("hwmonitor");
1164         child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1165         root->add_child_nocopy (*child);
1166
1167         child = new XMLNode ("hwmeter");
1168         child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1169         root->add_child_nocopy (*child);
1170
1171         child = new XMLNode ("verbose");
1172         child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1173         root->add_child_nocopy (*child);
1174
1175         child = new XMLNode ("samplerate");
1176         child->add_property ("val", sample_rate_combo.get_active_text());
1177         root->add_child_nocopy (*child);
1178
1179         child = new XMLNode ("periodsize");
1180         child->add_property ("val", period_size_combo.get_active_text());
1181         root->add_child_nocopy (*child);
1182
1183         child = new XMLNode ("serverpath");
1184         child->add_property ("val", serverpath_combo.get_active_text());
1185         root->add_child_nocopy (*child);
1186
1187         child = new XMLNode ("driver");
1188         child->add_property ("val", driver_combo.get_active_text());
1189         root->add_child_nocopy (*child);
1190
1191         child = new XMLNode ("interface");
1192         child->add_property ("val", interface_combo.get_active_text());
1193         root->add_child_nocopy (*child);
1194
1195         child = new XMLNode ("timeout");
1196         child->add_property ("val", timeout_combo.get_active_text());
1197         root->add_child_nocopy (*child);
1198
1199         child = new XMLNode ("dither");
1200         child->add_property ("val", dither_mode_combo.get_active_text());
1201         root->add_child_nocopy (*child);
1202
1203         child = new XMLNode ("audiomode");
1204         child->add_property ("val", audio_mode_combo.get_active_text());
1205         root->add_child_nocopy (*child);
1206
1207         child = new XMLNode ("inputdevice");
1208         child->add_property ("val", input_device_combo.get_active_text());
1209         root->add_child_nocopy (*child);
1210
1211         child = new XMLNode ("outputdevice");
1212         child->add_property ("val", output_device_combo.get_active_text());
1213         root->add_child_nocopy (*child);
1214
1215         child = new XMLNode ("mididriver");
1216         child->add_property ("val", midi_driver_combo.get_active_text());
1217         root->add_child_nocopy (*child);
1218
1219         return *root;
1220 }
1221
1222 void
1223 EngineControl::set_state (const XMLNode& root)
1224 {
1225         XMLNodeList          clist;
1226         XMLNodeConstIterator citer;
1227         XMLNode* child;
1228         XMLProperty* prop = NULL;
1229         bool using_dummy = false;
1230         bool using_ffado = false;
1231
1232         int val;
1233         string strval;
1234
1235         if ( (child = root.child ("driver"))){
1236                 prop = child->property("val");
1237
1238                 if (prop && (prop->value() == "Dummy") ) {
1239                         using_dummy = true;
1240                 }
1241                 if (prop && (prop->value() == "FFADO") ) {
1242                         using_ffado = true;
1243                 }
1244
1245         }
1246
1247         clist = root.children();
1248
1249         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1250
1251                 child = *citer;
1252
1253                 prop = child->property ("val");
1254
1255                 if (!prop || prop->value().empty()) {
1256
1257                         if (((using_dummy || using_ffado)
1258                                 && ( child->name() == "interface"
1259                                         || child->name() == "inputdevice"
1260                                         || child->name() == "outputdevice"))
1261                                 || child->name() == "timeout")
1262                         {
1263                                 continue;
1264                         }
1265
1266                         error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1267                         continue;
1268                 }
1269
1270                 strval = prop->value();
1271
1272                 /* adjustments/spinners */
1273
1274                 if (child->name() == "periods") {
1275                         val = atoi (strval);
1276                         periods_adjustment.set_value(val);
1277                 } else if (child->name() == "priority") {
1278                         val = atoi (strval);
1279                         priority_adjustment.set_value(val);
1280                 } else if (child->name() == "ports") {
1281                         val = atoi (strval);
1282                         ports_adjustment.set_value(val);
1283                 } else if (child->name() == "inlatency") {
1284                         val = atoi (strval);
1285                         input_latency.set_value(val);
1286                 } else if (child->name() == "outlatency") {
1287                         val = atoi (strval);
1288                         output_latency.set_value(val);
1289                 }
1290
1291                 /* buttons */
1292
1293                 else if (child->name() == "realtime") {
1294                         val = atoi (strval);
1295                         realtime_button.set_active(val);
1296                 } else if (child->name() == "nomemorylock") {
1297                         val = atoi (strval);
1298                         no_memory_lock_button.set_active(val);
1299                 } else if (child->name() == "unlockmemory") {
1300                         val = atoi (strval);
1301                         unlock_memory_button.set_active(val);
1302                 } else if (child->name() == "softmode") {
1303                         val = atoi (strval);
1304                         soft_mode_button.set_active(val);
1305                 } else if (child->name() == "force16bit") {
1306                         val = atoi (strval);
1307                         force16bit_button.set_active(val);
1308                 } else if (child->name() == "hwmonitor") {
1309                         val = atoi (strval);
1310                         hw_monitor_button.set_active(val);
1311                 } else if (child->name() == "hwmeter") {
1312                         val = atoi (strval);
1313                         hw_meter_button.set_active(val);
1314                 } else if (child->name() == "verbose") {
1315                         val = atoi (strval);
1316                         verbose_output_button.set_active(val);
1317                 }
1318
1319                 /* combos */
1320
1321                 else if (child->name() == "samplerate") {
1322                         sample_rate_combo.set_active_text(strval);
1323                 } else if (child->name() == "periodsize") {
1324                         period_size_combo.set_active_text(strval);
1325                 } else if (child->name() == "serverpath") {
1326
1327                         /* only attempt to set this if we have bothered to look
1328                            up server names already. otherwise this is all
1329                            redundant (actually, all of this dialog/widget
1330                            is redundant in that case ...)
1331                         */
1332
1333                         if (!server_strings.empty()) {
1334                                 /* do not allow us to use a server path that doesn't
1335                                    exist on this system. this handles cases where
1336                                    the user has an RC file listing a serverpath
1337                                    from some other machine.
1338                                 */
1339                                 vector<string>::iterator x;
1340                                 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1341                                         if (*x == strval) {
1342                                                 break;
1343                                         }
1344                                 }
1345                                 if (x != server_strings.end()) {
1346                                         serverpath_combo.set_active_text (strval);
1347                                 } else {
1348                                         warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1349                                                                    strval)
1350                                                 << endmsg;
1351                                 }
1352                         }
1353
1354                 } else if (child->name() == "driver") {
1355                         driver_combo.set_active_text(strval);
1356                 } else if (child->name() == "interface") {
1357                         interface_combo.set_active_text(strval);
1358                 } else if (child->name() == "timeout") {
1359                         timeout_combo.set_active_text(strval);
1360                 } else if (child->name() == "dither") {
1361                         dither_mode_combo.set_active_text(strval);
1362                 } else if (child->name() == "audiomode") {
1363                         audio_mode_combo.set_active_text(strval);
1364                 } else if (child->name() == "inputdevice") {
1365                         input_device_combo.set_active_text(strval);
1366                 } else if (child->name() == "outputdevice") {
1367                         output_device_combo.set_active_text(strval);
1368                 } else if (child->name() == "mididriver") {
1369                         midi_driver_combo.set_active_text(strval);
1370                 }
1371         }
1372 }