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