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