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