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