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