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