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