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