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