Remove unnecessary 0 checks before delete; see http://www.parashift.com/c++-faq-lite...
[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 = 0;
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_freebob_devices ()
814 {
815         vector<string> devs;
816         return devs;
817 }
818 vector<string>
819 EngineControl::enumerate_oss_devices ()
820 {
821         vector<string> devs;
822         return devs;
823 }
824 vector<string>
825 EngineControl::enumerate_dummy_devices ()
826 {
827         vector<string> devs;
828         return devs;
829 }
830 vector<string>
831 EngineControl::enumerate_netjack_devices ()
832 {
833         vector<string> devs;
834         return devs;
835 }
836 #endif
837
838 void
839 EngineControl::driver_changed ()
840 {
841         string driver = driver_combo.get_active_text();
842         string::size_type maxlen = 0;
843         int maxindex = -1;
844         int n = 0;
845
846         enumerate_devices (driver);
847
848         vector<string>& strings = devices[driver];
849
850         if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
851                 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
852                 return;
853         }
854         
855         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
856                 if ((*i).length() > maxlen) {
857                         maxlen = (*i).length();
858                         maxindex = n;
859                 }
860         }
861
862         set_popdown_strings (interface_combo, strings);
863         set_popdown_strings (input_device_combo, strings);
864         set_popdown_strings (output_device_combo, strings);
865
866         if (!strings.empty()) {
867                 interface_combo.set_active_text (strings.front());
868                 input_device_combo.set_active_text (strings.front());
869                 output_device_combo.set_active_text (strings.front());
870         } 
871         
872         if (driver == "ALSA") {
873                 soft_mode_button.set_sensitive (true);
874                 force16bit_button.set_sensitive (true);
875                 hw_monitor_button.set_sensitive (true);
876                 hw_meter_button.set_sensitive (true);
877                 monitor_button.set_sensitive (true);
878         } else {
879                 soft_mode_button.set_sensitive (false);
880                 force16bit_button.set_sensitive (false);
881                 hw_monitor_button.set_sensitive (false);
882                 hw_meter_button.set_sensitive (false);
883                 monitor_button.set_sensitive (false);
884         }
885 }
886
887 uint32_t
888 EngineControl::get_rate ()
889 {
890         return atoi (sample_rate_combo.get_active_text ());
891 }
892
893 void
894 EngineControl::redisplay_latency ()
895 {
896         uint32_t rate = get_rate();
897 #ifdef __APPLE_
898         float periods = 2;
899 #else
900         float periods = periods_adjustment.get_value();
901 #endif
902         float period_size = atof (period_size_combo.get_active_text());
903
904         char buf[32];
905         snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
906
907         latency_label.set_text (buf);
908 }
909
910 void
911 EngineControl::audio_mode_changed ()
912 {
913         Glib::ustring str = audio_mode_combo.get_active_text();
914
915         if (str == _("Playback/Recording on 1 Device")) {
916                 input_device_combo.set_sensitive (false);
917                 output_device_combo.set_sensitive (false);
918         } else if (str == _("Playback/Recording on 2 Devices")) {
919                 input_device_combo.set_sensitive (true);
920                 output_device_combo.set_sensitive (true);
921         } else if (str == _("Playback only")) {
922                 output_device_combo.set_sensitive (true);
923         } else if (str == _("Recording only")) {
924                 input_device_combo.set_sensitive (true);
925         }
926 }
927
928 static bool jack_server_filter(const string& str, void *arg)
929 {
930    return str == "jackd" || str == "jackdmp";
931 }
932
933 void
934 EngineControl::find_jack_servers (vector<string>& strings)
935 {
936 #ifdef __APPLE__
937         /* this magic lets us finds the path to the OSX bundle, and then
938            we infer JACK's location from there
939         */
940         
941         char execpath[MAXPATHLEN+1];
942         uint32_t pathsz = sizeof (execpath);
943
944         _NSGetExecutablePath (execpath, &pathsz);
945         
946         string path (Glib::path_get_dirname (execpath));
947         path += "/jackd";
948
949         if (Glib::file_test (path, FILE_TEST_EXISTS)) {
950                 strings.push_back (path);
951         } 
952
953         if (getenv ("ARDOUR_WITH_JACK")) {
954                 /* no other options - only use the JACK we supply */
955                 if (strings.empty()) {
956                         fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
957                         /*NOTREACHED*/
958                 }
959                 return;
960         }
961 #else
962         string path;
963 #endif
964         
965         PathScanner scanner;
966         vector<string *> *jack_servers;
967         std::map<string,int> un;
968         char *p;
969         bool need_minimal_path = false;
970
971         p = getenv ("PATH");
972
973         if (p && *p) {
974                 path = p;
975         } else {
976                 need_minimal_path = true;
977         }
978
979 #ifdef __APPLE__
980         // many mac users don't have PATH set up to include
981         // likely installed locations of JACK
982         need_minimal_path = true;
983 #endif
984
985         if (need_minimal_path) {
986                 if (path.empty()) {
987                         path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
988                 } else {
989                         path += ":/usr/local/bin:/opt/local/bin";
990                 }
991         }
992
993 #ifdef __APPLE__
994         // push it back into the environment so that auto-started JACK can find it.
995         // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
996         setenv ("PATH", path.c_str(), 1);
997 #endif
998
999         jack_servers = scanner (path, jack_server_filter, 0, false, true);
1000         
1001         vector<string *>::iterator iter;
1002         
1003         for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1004                 string p = **iter;
1005                 
1006                 if (un[p]++ == 0) {
1007                         strings.push_back(p);
1008                 }
1009         }
1010 }
1011
1012
1013 string
1014 EngineControl::get_device_name (const string& driver, const string& human_readable)
1015 {
1016         vector<string>::iterator n;
1017         vector<string>::iterator i;
1018
1019         if (human_readable.empty()) {
1020                 /* this can happen if the user's .ardourrc file has a device name from
1021                    another computer system in it
1022                 */
1023                 MessageDialog msg (_("You need to choose an audio device first."));
1024                 msg.run ();
1025                 return string();
1026         }
1027
1028         if (backend_devs.empty()) {
1029                 return human_readable;
1030         }
1031         
1032         for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1033                 if (human_readable == (*i)) {
1034                         return (*n);
1035                 }
1036         }
1037         
1038         if (i == devices[driver].end()) {
1039                 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1040         }
1041
1042         return string();
1043 }
1044
1045 XMLNode&
1046 EngineControl::get_state ()
1047 {
1048         XMLNode* root = new XMLNode ("AudioSetup");
1049         XMLNode* child;
1050         Glib::ustring path;
1051
1052         child = new XMLNode ("periods");
1053         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1054         root->add_child_nocopy (*child);
1055
1056         child = new XMLNode ("priority");
1057         child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1058         root->add_child_nocopy (*child);
1059
1060         child = new XMLNode ("ports");
1061         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1062         root->add_child_nocopy (*child);
1063
1064         child = new XMLNode ("inchannels");
1065         child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1066         root->add_child_nocopy (*child);
1067
1068         child = new XMLNode ("outchannels");
1069         child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1070         root->add_child_nocopy (*child);
1071
1072         child = new XMLNode ("inlatency");
1073         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1074         root->add_child_nocopy (*child);
1075
1076         child = new XMLNode ("outlatency");
1077         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1078         root->add_child_nocopy (*child);
1079
1080         child = new XMLNode ("realtime");
1081         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1082         root->add_child_nocopy (*child);
1083
1084         child = new XMLNode ("nomemorylock");
1085         child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1086         root->add_child_nocopy (*child);
1087
1088         child = new XMLNode ("unlockmemory");
1089         child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1090         root->add_child_nocopy (*child);
1091
1092         child = new XMLNode ("softmode");
1093         child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1094         root->add_child_nocopy (*child);
1095
1096         child = new XMLNode ("force16bit");
1097         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1098         root->add_child_nocopy (*child);
1099
1100         child = new XMLNode ("hwmonitor");
1101         child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1102         root->add_child_nocopy (*child);
1103
1104         child = new XMLNode ("hwmeter");
1105         child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1106         root->add_child_nocopy (*child);
1107
1108         child = new XMLNode ("verbose");
1109         child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1110         root->add_child_nocopy (*child);
1111
1112         child = new XMLNode ("samplerate");
1113         child->add_property ("val", sample_rate_combo.get_active_text());
1114         root->add_child_nocopy (*child);
1115
1116         child = new XMLNode ("periodsize");
1117         child->add_property ("val", period_size_combo.get_active_text());
1118         root->add_child_nocopy (*child);
1119
1120         child = new XMLNode ("serverpath");
1121         child->add_property ("val", serverpath_combo.get_active_text());
1122         root->add_child_nocopy (*child);
1123
1124         child = new XMLNode ("driver");
1125         child->add_property ("val", driver_combo.get_active_text());
1126         root->add_child_nocopy (*child);
1127
1128         child = new XMLNode ("interface");
1129         child->add_property ("val", interface_combo.get_active_text());
1130         root->add_child_nocopy (*child);
1131
1132         child = new XMLNode ("timeout");
1133         child->add_property ("val", timeout_combo.get_active_text());
1134         root->add_child_nocopy (*child);
1135
1136         child = new XMLNode ("dither");
1137         child->add_property ("val", dither_mode_combo.get_active_text());
1138         root->add_child_nocopy (*child);
1139
1140         child = new XMLNode ("audiomode");
1141         child->add_property ("val", audio_mode_combo.get_active_text());
1142         root->add_child_nocopy (*child);
1143
1144         child = new XMLNode ("inputdevice");
1145         child->add_property ("val", input_device_combo.get_active_text());
1146         root->add_child_nocopy (*child);
1147
1148         child = new XMLNode ("outputdevice");
1149         child->add_property ("val", output_device_combo.get_active_text());
1150         root->add_child_nocopy (*child);
1151         
1152         return *root;
1153 }
1154
1155 void
1156 EngineControl::set_state (const XMLNode& root)
1157 {
1158         XMLNodeList          clist;
1159         XMLNodeConstIterator citer;
1160         XMLNode* child;
1161         XMLProperty* prop = NULL;
1162         bool using_dummy = false;
1163         
1164         int val;
1165         string strval;
1166         
1167         if ( (child = root.child ("driver"))){
1168                 prop = child->property("val");
1169                 if (prop && (prop->value() == "Dummy") ) {
1170                         using_dummy = true;
1171                 }
1172         }
1173         
1174         clist = root.children();
1175
1176         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1177                 if ( prop && (prop->value() == "FFADO" ))
1178                                 continue;
1179                 child = *citer;
1180
1181                 prop = child->property ("val");
1182
1183                 if (!prop || prop->value().empty()) {
1184
1185                         if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1186                                 continue;
1187                         error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1188                         continue;
1189                 }
1190                 
1191                 strval = prop->value();
1192
1193                 /* adjustments/spinners */
1194
1195                 if (child->name() == "periods") {
1196                         val = atoi (strval);
1197                         periods_adjustment.set_value(val);
1198                 } else if (child->name() == "priority") {
1199                         val = atoi (strval);
1200                         priority_adjustment.set_value(val);
1201                 } else if (child->name() == "ports") {
1202                         val = atoi (strval);
1203                         ports_adjustment.set_value(val);
1204                 } else if (child->name() == "inchannels") {
1205                         val = atoi (strval);
1206                         input_channels.set_value(val);
1207                 } else if (child->name() == "outchannels") {
1208                         val = atoi (strval);
1209                         output_channels.set_value(val);
1210                 } else if (child->name() == "inlatency") {
1211                         val = atoi (strval);
1212                         input_latency.set_value(val);
1213                 } else if (child->name() == "outlatency") {
1214                         val = atoi (strval);
1215                         output_latency.set_value(val);
1216                 }
1217
1218                 /* buttons */
1219
1220                 else if (child->name() == "realtime") {
1221                         val = atoi (strval);
1222                         realtime_button.set_active(val);
1223                 } else if (child->name() == "nomemorylock") {
1224                         val = atoi (strval);
1225                         no_memory_lock_button.set_active(val);
1226                 } else if (child->name() == "unlockmemory") {
1227                         val = atoi (strval);
1228                         unlock_memory_button.set_active(val);
1229                 } else if (child->name() == "softmode") {
1230                         val = atoi (strval);
1231                         soft_mode_button.set_active(val);
1232                 } else if (child->name() == "force16bit") {
1233                         val = atoi (strval);
1234                         force16bit_button.set_active(val);
1235                 } else if (child->name() == "hwmonitor") {
1236                         val = atoi (strval);
1237                         hw_monitor_button.set_active(val);
1238                 } else if (child->name() == "hwmeter") {
1239                         val = atoi (strval);
1240                         hw_meter_button.set_active(val);
1241                 } else if (child->name() == "verbose") {
1242                         val = atoi (strval);
1243                         verbose_output_button.set_active(val);
1244                 }
1245
1246                 /* combos */
1247
1248                 else if (child->name() == "samplerate") {
1249                         sample_rate_combo.set_active_text(strval);
1250                 } else if (child->name() == "periodsize") {
1251                         period_size_combo.set_active_text(strval);
1252                 } else if (child->name() == "serverpath") {
1253                         /* do not allow us to use a server path that doesn't
1254                            exist on this system. this handles cases where
1255                            the user has an RC file listing a serverpath
1256                            from some other machine.
1257                         */
1258                         vector<string>::iterator x;
1259                         for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1260                                 if (*x == strval) {
1261                                         break;
1262                                 }
1263                         }
1264                         if (x != server_strings.end()) {
1265                                 serverpath_combo.set_active_text (strval);
1266                         } else {
1267                                 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1268                                                              strval)
1269                                         << endmsg;
1270                         }
1271                 } else if (child->name() == "driver") {
1272                         driver_combo.set_active_text(strval);
1273                 } else if (child->name() == "interface") {
1274                         interface_combo.set_active_text(strval);
1275                 } else if (child->name() == "timeout") {
1276                         timeout_combo.set_active_text(strval);
1277                 } else if (child->name() == "dither") {
1278                         dither_mode_combo.set_active_text(strval);
1279                 } else if (child->name() == "audiomode") {
1280                         audio_mode_combo.set_active_text(strval);
1281                 } else if (child->name() == "inputdevice") {
1282                         input_device_combo.set_active_text(strval);
1283                 } else if (child->name() == "outputdevice") {
1284                         output_device_combo.set_active_text(strval);
1285                 }
1286         }
1287 }