changes to help strp silence
[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 (sigc::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 (sigc::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 (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
176         periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
177         period_size_combo.signal_changed().connect (sigc::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 (sigc::mem_fun (*this, &EngineControl::start_engine));
202         stop_button.signal_clicked().connect (sigc::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.set_active (true);
219         realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
220         realtime_changed ();
221
222 #if PROVIDE_TOO_MANY_OPTIONS
223
224 #ifndef __APPLE__
225         label = manage (new Label (_("Realtime Priority")));
226         label->set_alignment (1.0, 0.5);
227         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
228         options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
229         ++row;
230         priority_spinner.set_value (60);
231
232         options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
233         ++row;
234         options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
235         ++row;
236         options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
237         ++row;
238         options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
239         ++row;
240         options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
241         ++row;
242         options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
243         ++row;
244         options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
245         ++row;
246         options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
247         ++row;
248 #else
249         options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
250         ++row;
251 #endif
252
253         strings.clear ();
254         strings.push_back (_("Ignore"));
255         strings.push_back ("500 msec");
256         strings.push_back ("1 sec");
257         strings.push_back ("2 sec");
258         strings.push_back ("10 sec");
259         set_popdown_strings (timeout_combo, strings);
260         timeout_combo.set_active_text (strings.front ());
261
262         label = manage (new Label (_("Client timeout")));
263         label->set_alignment (1.0, 0.5);
264         options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
265         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266         ++row;
267
268 #endif /* PROVIDE_TOO_MANY_OPTIONS */
269         label = manage (new Label (_("Number of ports")));
270         label->set_alignment (1.0, 0.5);
271         options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
272         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
273         ++row;
274
275 #ifndef __APPLE__
276         label = manage (new Label (_("Dither")));
277         label->set_alignment (1.0, 0.5);
278         options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
279         options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280         ++row;
281 #endif
282
283         find_jack_servers (server_strings);
284
285         if (server_strings.empty()) {
286                 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
287                 /*NOTREACHED*/
288         }
289
290         set_popdown_strings (serverpath_combo, server_strings);
291         serverpath_combo.set_active_text (server_strings.front());
292
293         if (server_strings.size() > 1) {
294                 label = manage (new Label (_("Server:")));
295                 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
296                 label->set_alignment (0.0, 0.5);
297                 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
298                 ++row;
299         }
300
301         /* device settings */
302
303         device_packer.set_spacings (6);
304         row = 0;
305
306 #ifndef __APPLE__
307         label = manage (new Label (_("Input device")));
308         label->set_alignment (1.0, 0.5);
309         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
310         device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
311         ++row;
312         label = manage (new Label (_("Output device")));
313         label->set_alignment (1.0, 0.5);
314         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
315         device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
316         ++row;
317 #endif
318         label = manage (new Label (_("Input channels")));
319         label->set_alignment (1.0, 0.5);
320         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
321         device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
322         ++row;
323         label = manage (new Label (_("Output channels")));
324         label->set_alignment (1.0, 0.5);
325         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
326         device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
327         ++row;
328         label = manage (new Label (_("Hardware input latency (samples)")));
329         label->set_alignment (1.0, 0.5);
330         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
331         device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
332         ++row;
333         label = manage (new Label (_("Hardware output latency (samples)")));
334         label->set_alignment (1.0, 0.5);
335         device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
336         device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
337         ++row;
338
339         basic_hbox.pack_start (basic_packer, false, false);
340         options_hbox.pack_start (options_packer, false, false);
341
342         device_packer.set_border_width (12);
343         options_packer.set_border_width (12);
344         basic_packer.set_border_width (12);
345
346         notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
347         notebook.pages().push_back (TabElem (options_hbox, _("Options")));
348         notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
349         notebook.set_border_width (12);
350
351         set_border_width (12);
352         pack_start (notebook);
353 }
354
355 EngineControl::~EngineControl ()
356 {
357
358 }
359
360 void
361 EngineControl::build_command_line (vector<string>& cmd)
362 {
363         string str;
364         string driver;
365         bool using_oss = false;
366         bool using_alsa = false;
367         bool using_coreaudio = false;
368         bool using_netjack = false;
369         bool using_ffado = false;
370         bool using_dummy = false;
371
372         /* first, path to jackd */
373
374         cmd.push_back (serverpath_combo.get_active_text ());
375
376         /* now jackd arguments */
377
378         str = timeout_combo.get_active_text ();
379         if (str != _("Ignore")) {
380                 double secs = 0;
381                 uint32_t msecs;
382                 secs = atof (str);
383                 msecs = (uint32_t) floor (secs * 1000.0);
384                 if (msecs > 0) {
385                         cmd.push_back ("-t");
386                         cmd.push_back (to_string (msecs, std::dec));
387                 }
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_freebob_devices ()
820 {
821         vector<string> devs;
822         return devs;
823 }
824 vector<string>
825 EngineControl::enumerate_oss_devices ()
826 {
827         vector<string> devs;
828         return devs;
829 }
830 vector<string>
831 EngineControl::enumerate_dummy_devices ()
832 {
833         vector<string> devs;
834         return devs;
835 }
836 vector<string>
837 EngineControl::enumerate_netjack_devices ()
838 {
839         vector<string> devs;
840         return devs;
841 }
842 #endif
843
844 void
845 EngineControl::driver_changed ()
846 {
847         string driver = driver_combo.get_active_text();
848         string::size_type maxlen = 0;
849         int maxindex = -1;
850         int n = 0;
851
852         enumerate_devices (driver);
853
854         vector<string>& strings = devices[driver];
855
856         if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
857                 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
858                 return;
859         }
860
861         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
862                 if ((*i).length() > maxlen) {
863                         maxlen = (*i).length();
864                         maxindex = n;
865                 }
866         }
867
868         set_popdown_strings (interface_combo, strings);
869         set_popdown_strings (input_device_combo, strings);
870         set_popdown_strings (output_device_combo, strings);
871
872         if (!strings.empty()) {
873                 interface_combo.set_active_text (strings.front());
874                 input_device_combo.set_active_text (strings.front());
875                 output_device_combo.set_active_text (strings.front());
876         }
877
878         if (driver == "ALSA") {
879                 soft_mode_button.set_sensitive (true);
880                 force16bit_button.set_sensitive (true);
881                 hw_monitor_button.set_sensitive (true);
882                 hw_meter_button.set_sensitive (true);
883                 monitor_button.set_sensitive (true);
884         } else {
885                 soft_mode_button.set_sensitive (false);
886                 force16bit_button.set_sensitive (false);
887                 hw_monitor_button.set_sensitive (false);
888                 hw_meter_button.set_sensitive (false);
889                 monitor_button.set_sensitive (false);
890         }
891 }
892
893 uint32_t
894 EngineControl::get_rate ()
895 {
896         return atoi (sample_rate_combo.get_active_text ());
897 }
898
899 void
900 EngineControl::redisplay_latency ()
901 {
902         uint32_t rate = get_rate();
903 #ifdef __APPLE_
904         float periods = 2;
905 #else
906         float periods = periods_adjustment.get_value();
907 #endif
908         float period_size = atof (period_size_combo.get_active_text());
909
910         char buf[32];
911         snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
912
913         latency_label.set_text (buf);
914 }
915
916 void
917 EngineControl::audio_mode_changed ()
918 {
919         Glib::ustring str = audio_mode_combo.get_active_text();
920
921         if (str == _("Playback/Recording on 1 Device")) {
922                 input_device_combo.set_sensitive (false);
923                 output_device_combo.set_sensitive (false);
924         } else if (str == _("Playback/Recording on 2 Devices")) {
925                 input_device_combo.set_sensitive (true);
926                 output_device_combo.set_sensitive (true);
927         } else if (str == _("Playback only")) {
928                 output_device_combo.set_sensitive (true);
929         } else if (str == _("Recording only")) {
930                 input_device_combo.set_sensitive (true);
931         }
932 }
933
934 static bool jack_server_filter(const string& str, void */*arg*/)
935 {
936    return str == "jackd" || str == "jackdmp";
937 }
938
939 void
940 EngineControl::find_jack_servers (vector<string>& strings)
941 {
942 #ifdef __APPLE__
943         /* this magic lets us finds the path to the OSX bundle, and then
944            we infer JACK's location from there
945         */
946
947         char execpath[MAXPATHLEN+1];
948         uint32_t pathsz = sizeof (execpath);
949
950         _NSGetExecutablePath (execpath, &pathsz);
951
952         string path (Glib::path_get_dirname (execpath));
953         path += "/jackd";
954
955         if (Glib::file_test (path, FILE_TEST_EXISTS)) {
956                 strings.push_back (path);
957         }
958
959         if (getenv ("ARDOUR_WITH_JACK")) {
960                 /* no other options - only use the JACK we supply */
961                 if (strings.empty()) {
962                         fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
963                         /*NOTREACHED*/
964                 }
965                 return;
966         }
967 #else
968         string path;
969 #endif
970
971         PathScanner scanner;
972         vector<string *> *jack_servers;
973         std::map<string,int> un;
974         char *p;
975         bool need_minimal_path = false;
976
977         p = getenv ("PATH");
978
979         if (p && *p) {
980                 path = p;
981         } else {
982                 need_minimal_path = true;
983         }
984
985 #ifdef __APPLE__
986         // many mac users don't have PATH set up to include
987         // likely installed locations of JACK
988         need_minimal_path = true;
989 #endif
990
991         if (need_minimal_path) {
992                 if (path.empty()) {
993                         path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
994                 } else {
995                         path += ":/usr/local/bin:/opt/local/bin";
996                 }
997         }
998
999 #ifdef __APPLE__
1000         // push it back into the environment so that auto-started JACK can find it.
1001         // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1002         setenv ("PATH", path.c_str(), 1);
1003 #endif
1004
1005         jack_servers = scanner (path, jack_server_filter, 0, false, true);
1006
1007         vector<string *>::iterator iter;
1008
1009         for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1010                 string p = **iter;
1011
1012                 if (un[p]++ == 0) {
1013                         strings.push_back(p);
1014                 }
1015         }
1016 }
1017
1018
1019 string
1020 EngineControl::get_device_name (const string& driver, const string& human_readable)
1021 {
1022         vector<string>::iterator n;
1023         vector<string>::iterator i;
1024
1025         if (human_readable.empty()) {
1026                 /* this can happen if the user's .ardourrc file has a device name from
1027                    another computer system in it
1028                 */
1029                 MessageDialog msg (_("You need to choose an audio device first."));
1030                 msg.run ();
1031                 return string();
1032         }
1033
1034         if (backend_devs.empty()) {
1035                 return human_readable;
1036         }
1037
1038         for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1039                 if (human_readable == (*i)) {
1040                         return (*n);
1041                 }
1042         }
1043
1044         if (i == devices[driver].end()) {
1045                 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1046         }
1047
1048         return string();
1049 }
1050
1051 XMLNode&
1052 EngineControl::get_state ()
1053 {
1054         XMLNode* root = new XMLNode ("AudioSetup");
1055         XMLNode* child;
1056         Glib::ustring path;
1057
1058         child = new XMLNode ("periods");
1059         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1060         root->add_child_nocopy (*child);
1061
1062         child = new XMLNode ("priority");
1063         child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1064         root->add_child_nocopy (*child);
1065
1066         child = new XMLNode ("ports");
1067         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1068         root->add_child_nocopy (*child);
1069
1070         child = new XMLNode ("inchannels");
1071         child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1072         root->add_child_nocopy (*child);
1073
1074         child = new XMLNode ("outchannels");
1075         child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1076         root->add_child_nocopy (*child);
1077
1078         child = new XMLNode ("inlatency");
1079         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1080         root->add_child_nocopy (*child);
1081
1082         child = new XMLNode ("outlatency");
1083         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1084         root->add_child_nocopy (*child);
1085
1086         child = new XMLNode ("realtime");
1087         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1088         root->add_child_nocopy (*child);
1089
1090         child = new XMLNode ("nomemorylock");
1091         child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1092         root->add_child_nocopy (*child);
1093
1094         child = new XMLNode ("unlockmemory");
1095         child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1096         root->add_child_nocopy (*child);
1097
1098         child = new XMLNode ("softmode");
1099         child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1100         root->add_child_nocopy (*child);
1101
1102         child = new XMLNode ("force16bit");
1103         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1104         root->add_child_nocopy (*child);
1105
1106         child = new XMLNode ("hwmonitor");
1107         child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1108         root->add_child_nocopy (*child);
1109
1110         child = new XMLNode ("hwmeter");
1111         child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1112         root->add_child_nocopy (*child);
1113
1114         child = new XMLNode ("verbose");
1115         child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1116         root->add_child_nocopy (*child);
1117
1118         child = new XMLNode ("samplerate");
1119         child->add_property ("val", sample_rate_combo.get_active_text());
1120         root->add_child_nocopy (*child);
1121
1122         child = new XMLNode ("periodsize");
1123         child->add_property ("val", period_size_combo.get_active_text());
1124         root->add_child_nocopy (*child);
1125
1126         child = new XMLNode ("serverpath");
1127         child->add_property ("val", serverpath_combo.get_active_text());
1128         root->add_child_nocopy (*child);
1129
1130         child = new XMLNode ("driver");
1131         child->add_property ("val", driver_combo.get_active_text());
1132         root->add_child_nocopy (*child);
1133
1134         child = new XMLNode ("interface");
1135         child->add_property ("val", interface_combo.get_active_text());
1136         root->add_child_nocopy (*child);
1137
1138         child = new XMLNode ("timeout");
1139         child->add_property ("val", timeout_combo.get_active_text());
1140         root->add_child_nocopy (*child);
1141
1142         child = new XMLNode ("dither");
1143         child->add_property ("val", dither_mode_combo.get_active_text());
1144         root->add_child_nocopy (*child);
1145
1146         child = new XMLNode ("audiomode");
1147         child->add_property ("val", audio_mode_combo.get_active_text());
1148         root->add_child_nocopy (*child);
1149
1150         child = new XMLNode ("inputdevice");
1151         child->add_property ("val", input_device_combo.get_active_text());
1152         root->add_child_nocopy (*child);
1153
1154         child = new XMLNode ("outputdevice");
1155         child->add_property ("val", output_device_combo.get_active_text());
1156         root->add_child_nocopy (*child);
1157
1158         return *root;
1159 }
1160
1161 void
1162 EngineControl::set_state (const XMLNode& root)
1163 {
1164         XMLNodeList          clist;
1165         XMLNodeConstIterator citer;
1166         XMLNode* child;
1167         XMLProperty* prop = NULL;
1168         bool using_dummy = false;
1169
1170         int val;
1171         string strval;
1172
1173         if ( (child = root.child ("driver"))){
1174                 prop = child->property("val");
1175                 if (prop && (prop->value() == "Dummy") ) {
1176                         using_dummy = true;
1177                 }
1178         }
1179
1180         clist = root.children();
1181
1182         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1183
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 }