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