Fix region/track naming on MIDI import (remove ugly/unnecessary %a %b etc suffix).
[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                 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                         cmd.push_back ("-C");
456                         cmd.push_back (get_device_name (driver, input_device_combo.get_active_text()));
457                         cmd.push_back ("-P");
458                         cmd.push_back (get_device_name (driver, output_device_combo.get_active_text()));
459                         
460                 } else if (str == _("Playback only")) {
461                         cmd.push_back ("-P");
462                 } else if (str == _("Recording only")) {
463                         cmd.push_back ("-C");
464                 }
465
466                 if (! using_dummy ) {
467                         cmd.push_back ("-n");
468                         cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
469                 }
470         }
471
472         cmd.push_back ("-r");
473         cmd.push_back (to_string (get_rate(), std::dec));
474         
475         cmd.push_back ("-p");
476         cmd.push_back (period_size_combo.get_active_text());
477
478         if (using_alsa) {
479                 
480                 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
481                         cmd.push_back ("-d");
482                         cmd.push_back (get_device_name (driver, interface_combo.get_active_text()));
483                 } 
484
485                 if (hw_meter_button.get_active()) {
486                         cmd.push_back ("-M");
487                 }
488                 
489                 if (hw_monitor_button.get_active()) {
490                         cmd.push_back ("-H");
491                 }
492
493                 str = dither_mode_combo.get_active_text();
494
495                 if (str == _("None")) {
496                 } else if (str == _("Triangular")) {
497                         cmd.push_back ("-z triangular");
498                 } else if (str == _("Rectangular")) {
499                         cmd.push_back ("-z rectangular");
500                 } else if (str == _("Shaped")) {
501                         cmd.push_back ("-z shaped");
502                 }
503
504                 if (force16bit_button.get_active()) {
505                         cmd.push_back ("-S");
506                 }
507                 
508                 if (soft_mode_button.get_active()) {
509                         cmd.push_back ("-s");
510                 }
511
512         } else if (using_coreaudio) {
513
514 #ifdef __APPLE__
515                 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
516                 cmd.push_back ("-d");
517                 cmd.push_back (get_device_name (driver, interface_combo.get_active_text()));
518 #endif
519
520         } else if (using_oss) {
521
522         } else if (using_netjack) {
523
524         }
525 }
526
527 bool
528 EngineControl::engine_running ()
529 {
530         jack_status_t status;
531         jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
532
533         if (status == 0) {
534                 jack_client_close (c);
535                 return true;
536         }
537         return false;
538 }
539
540 int
541 EngineControl::setup_engine ()
542 {
543         vector<string> args;
544         std::string cwd = "/tmp";
545
546         build_command_line (args);
547
548         Glib::ustring jackdrc_path = Glib::get_home_dir();
549         jackdrc_path += "/.jackdrc";
550
551         ofstream jackdrc (jackdrc_path.c_str());
552         if (!jackdrc) {
553                 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
554                 return -1;
555         }
556         cerr << "JACK COMMAND: ";
557         for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
558                 cerr << (*i) << ' ';
559                 jackdrc << (*i) << ' ';
560         }
561         cerr << endl;
562         jackdrc << endl;
563         jackdrc.close ();
564
565         _used = true;
566
567         return 0;
568 }
569
570 void
571 EngineControl::realtime_changed ()
572 {
573 #ifndef __APPLE__
574         priority_spinner.set_sensitive (realtime_button.get_active());
575 #endif
576 }
577
578 void
579 EngineControl::enumerate_devices (const string& driver)
580 {
581         /* note: case matters for the map keys */
582
583         if (driver == "CoreAudio") {
584 #ifdef __APPLE__                
585                 devices[driver] = enumerate_coreaudio_devices ();
586 #endif
587
588 #ifndef __APPLE__
589         } else if (driver == "ALSA") {
590                 devices[driver] = enumerate_alsa_devices ();
591         } else if (driver == "FFADO") {
592                 devices[driver] = enumerate_ffado_devices ();
593         } else if (driver == "OSS") {
594                 devices[driver] = enumerate_oss_devices ();
595         } else if (driver == "Dummy") {
596                 devices[driver] = enumerate_dummy_devices ();
597         } else if (driver == "NetJACK") {
598                 devices[driver] = enumerate_netjack_devices ();
599         }
600 #else
601         }
602 #endif
603 }
604
605 #ifdef __APPLE__
606 static OSStatus 
607 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
608 {
609         UInt32 size = sizeof(CFStringRef);
610         CFStringRef UI;
611         OSStatus res = AudioDeviceGetProperty(id, 0, false,
612                 kAudioDevicePropertyDeviceUID, &size, &UI);
613         if (res == noErr) 
614                 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
615         CFRelease(UI);
616         return res;
617 }
618
619 vector<string>
620 EngineControl::enumerate_coreaudio_devices ()
621 {
622         vector<string> devs;
623         
624         // Find out how many Core Audio devices are there, if any...
625         // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
626         OSStatus err;
627         Boolean isWritable;
628         size_t outSize = sizeof(isWritable);
629
630         backend_devs.clear ();
631
632         err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
633                                            &outSize, &isWritable);
634         if (err == noErr) {
635                 // Calculate the number of device available...
636                 int numCoreDevices = outSize / sizeof(AudioDeviceID);
637                 // Make space for the devices we are about to get...
638                 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
639                 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
640                                                &outSize, (void *) coreDeviceIDs);
641                 if (err == noErr) {
642                         // Look for the CoreAudio device name...
643                         char coreDeviceName[256];
644                         size_t nameSize;
645
646                         for (int i = 0; i < numCoreDevices; i++) {
647
648                                 nameSize = sizeof (coreDeviceName);
649
650                                 /* enforce duplex devices only */
651
652                                 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
653                                                                  0, true, kAudioDevicePropertyStreams,
654                                                                  &outSize, &isWritable);
655
656                                 if (err != noErr || outSize == 0) {
657                                         continue;
658                                 }
659
660                                 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
661                                                                  0, false, kAudioDevicePropertyStreams,
662                                                                  &outSize, &isWritable);
663
664                                 if (err != noErr || outSize == 0) {
665                                         continue;
666                                 }
667
668                                 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
669                                                                  0, true, kAudioDevicePropertyDeviceName,
670                                                                  &outSize, &isWritable);
671                                 if (err == noErr) {
672                                         err = AudioDeviceGetProperty(coreDeviceIDs[i],
673                                                                      0, true, kAudioDevicePropertyDeviceName,
674                                                                      &nameSize, (void *) coreDeviceName);
675                                         if (err == noErr) {
676                                                 char drivername[128];
677
678                                                 // this returns the unique id for the device
679                                                 // that must be used on the commandline for jack
680                                                 
681                                                 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
682                                                         devs.push_back (coreDeviceName);
683                                                         backend_devs.push_back (drivername);
684                                                 } 
685                                         }
686                                 }
687                         }
688                 }
689                 delete [] coreDeviceIDs;
690         }
691
692
693         if (devs.size() == 0) {
694                 MessageDialog msg (_("\
695 You do not have any audio devices capable of\n\
696 simultaneous playback and recording.\n\n\
697 Please use Applications -> Utilities -> Audio MIDI Setup\n\
698 to create an \"aggregrate\" device, or install a suitable\n\
699 audio interface.\n\n\
700 Please send email to Apple and ask them why new Macs\n\
701 have no duplex audio device.\n\n\
702 Alternatively, if you really want just playback\n\
703 or recording but not both, start JACK before running\n\
704 Ardour and choose the relevant device then."
705                                            ), 
706                                    true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
707                 msg.set_title (_("No suitable audio devices"));
708                 msg.set_position (Gtk::WIN_POS_MOUSE);
709                 msg.run ();
710                 exit (1);
711         }
712
713
714         return devs;
715 }
716 #else
717 vector<string>
718 EngineControl::enumerate_alsa_devices ()
719 {
720         vector<string> devs;
721
722         snd_ctl_t *handle;
723         snd_ctl_card_info_t *info;
724         snd_pcm_info_t *pcminfo;
725         snd_ctl_card_info_alloca(&info);
726         snd_pcm_info_alloca(&pcminfo);
727         string devname;
728         int cardnum = -1;
729         int device = -1;
730
731         backend_devs.clear ();
732
733         while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
734
735                 devname = "hw:";
736                 devname += to_string (cardnum, std::dec);
737
738                 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
739
740                         while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
741
742                                 bool have_playback = false;
743                                 bool have_capture = false;
744
745                                 /* find duplex devices only */
746
747                                 snd_pcm_info_set_device (pcminfo, device);
748                                 snd_pcm_info_set_subdevice (pcminfo, 0);
749                                 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
750
751                                 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
752                                         have_capture = true;
753                                 }
754
755                                 snd_pcm_info_set_device (pcminfo, device);
756                                 snd_pcm_info_set_subdevice (pcminfo, 0);
757                                 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
758
759                                 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
760                                         have_playback = true;
761                                 }
762
763                                 if (have_capture && have_playback) {
764                                         devs.push_back (snd_pcm_info_get_name (pcminfo));
765                                         devname += ',';
766                                         devname += to_string (device, std::dec);
767                                         backend_devs.push_back (devname);
768                                 }
769                         }
770
771                         snd_ctl_close(handle);
772                 }
773         }
774
775         return devs;
776 }
777
778 vector<string>
779 EngineControl::enumerate_ffado_devices ()
780 {
781         vector<string> devs;
782         backend_devs.clear ();
783         return devs;
784 }
785
786 vector<string>
787 EngineControl::enumerate_freebob_devices ()
788 {
789         vector<string> devs;
790         return devs;
791 }
792 vector<string>
793 EngineControl::enumerate_oss_devices ()
794 {
795         vector<string> devs;
796         return devs;
797 }
798 vector<string>
799 EngineControl::enumerate_dummy_devices ()
800 {
801         vector<string> devs;
802         return devs;
803 }
804 vector<string>
805 EngineControl::enumerate_netjack_devices ()
806 {
807         vector<string> devs;
808         return devs;
809 }
810 #endif
811
812 void
813 EngineControl::driver_changed ()
814 {
815         string driver = driver_combo.get_active_text();
816         string::size_type maxlen = 0;
817         int maxindex = -1;
818         int n = 0;
819
820         enumerate_devices (driver);
821
822         vector<string>& strings = devices[driver];
823
824         if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
825                 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
826                 return;
827         }
828         
829         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
830                 if ((*i).length() > maxlen) {
831                         maxlen = (*i).length();
832                         maxindex = n;
833                 }
834         }
835
836         set_popdown_strings (interface_combo, strings);
837         set_popdown_strings (input_device_combo, strings);
838         set_popdown_strings (output_device_combo, strings);
839
840         if (!strings.empty()) {
841                 interface_combo.set_active_text (strings.front());
842                 input_device_combo.set_active_text (strings.front());
843                 output_device_combo.set_active_text (strings.front());
844         } 
845         
846         if (driver == "ALSA") {
847                 soft_mode_button.set_sensitive (true);
848                 force16bit_button.set_sensitive (true);
849                 hw_monitor_button.set_sensitive (true);
850                 hw_meter_button.set_sensitive (true);
851                 monitor_button.set_sensitive (true);
852         } else {
853                 soft_mode_button.set_sensitive (false);
854                 force16bit_button.set_sensitive (false);
855                 hw_monitor_button.set_sensitive (false);
856                 hw_meter_button.set_sensitive (false);
857                 monitor_button.set_sensitive (false);
858         }
859 }
860
861 uint32_t
862 EngineControl::get_rate ()
863 {
864         return atoi (sample_rate_combo.get_active_text ());
865 }
866
867 void
868 EngineControl::redisplay_latency ()
869 {
870         uint32_t rate = get_rate();
871 #ifdef __APPLE_
872         float periods = 2;
873 #else
874         float periods = periods_adjustment.get_value();
875 #endif
876         float period_size = atof (period_size_combo.get_active_text());
877
878         char buf[32];
879         snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
880
881         latency_label.set_text (buf);
882 }
883
884 void
885 EngineControl::audio_mode_changed ()
886 {
887         Glib::ustring str = audio_mode_combo.get_active_text();
888
889         if (str == _("Playback/Recording on 1 Device")) {
890                 input_device_combo.set_sensitive (false);
891                 output_device_combo.set_sensitive (false);
892         } else if (str == _("Playback/Recording on 2 Devices")) {
893                 input_device_combo.set_sensitive (true);
894                 output_device_combo.set_sensitive (true);
895         } else if (str == _("Playback only")) {
896                 output_device_combo.set_sensitive (true);
897         } else if (str == _("Recording only")) {
898                 input_device_combo.set_sensitive (true);
899         }
900 }
901
902 static bool jack_server_filter(const string& str, void *arg)
903 {
904    return str == "jackd" || str == "jackdmp";
905 }
906
907 void
908 EngineControl::find_jack_servers (vector<string>& strings)
909 {
910 #ifdef __APPLE__
911         /* this magic lets us finds the path to the OSX bundle, and then
912            we infer JACK's location from there
913         */
914         
915         char execpath[MAXPATHLEN+1];
916         uint32_t pathsz = sizeof (execpath);
917
918         _NSGetExecutablePath (execpath, &pathsz);
919         
920         string path (Glib::path_get_dirname (execpath));
921         path += "/jackd";
922
923         if (Glib::file_test (path, FILE_TEST_EXISTS)) {
924                 strings.push_back (path);
925         } 
926
927         if (getenv ("ARDOUR_WITH_JACK")) {
928                 /* no other options - only use the JACK we supply */
929                 if (strings.empty()) {
930                         fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
931                         /*NOTREACHED*/
932                 }
933                 return;
934         }
935 #else
936         string path;
937 #endif
938         
939         PathScanner scanner;
940         vector<string *> *jack_servers;
941         std::map<string,int> un;
942         char *p;
943         bool need_minimal_path = false;
944
945         p = getenv ("PATH");
946
947         if (p && *p) {
948                 path = p;
949         } else {
950                 need_minimal_path = true;
951         }
952
953 #ifdef __APPLE__
954         // many mac users don't have PATH set up to include
955         // likely installed locations of JACK
956         need_minimal_path = true;
957 #endif
958
959         if (need_minimal_path) {
960                 if (path.empty()) {
961                         path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
962                 } else {
963                         path += ":/usr/local/bin:/opt/local/bin";
964                 }
965         }
966
967 #ifdef __APPLE__
968         // push it back into the environment so that auto-started JACK can find it.
969         // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
970         setenv ("PATH", path.c_str(), 1);
971 #endif
972
973         jack_servers = scanner (path, jack_server_filter, 0, false, true);
974         
975         vector<string *>::iterator iter;
976         
977         for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
978                 string p = **iter;
979                 
980                 if (un[p]++ == 0) {
981                         strings.push_back(p);
982                 }
983         }
984 }
985
986
987 string
988 EngineControl::get_device_name (const string& driver, const string& human_readable)
989 {
990         vector<string>::iterator n;
991         vector<string>::iterator i;
992
993         if (backend_devs.empty()) {
994                 return human_readable;
995         }
996         
997         for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
998                 if (human_readable == (*i)) {
999                         return (*n);
1000                 }
1001         }
1002         
1003         if (i == devices[driver].end()) {
1004                 fatal << string_compose (_("programming error: %1"), "true hardware name for ID missing") << endmsg;
1005                 /*NOTREACHED*/
1006         }
1007
1008         /* keep gcc happy */
1009
1010         return string();
1011 }
1012
1013 XMLNode&
1014 EngineControl::get_state ()
1015 {
1016         XMLNode* root = new XMLNode ("AudioSetup");
1017         XMLNode* child;
1018         Glib::ustring path;
1019
1020         child = new XMLNode ("periods");
1021         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1022         root->add_child_nocopy (*child);
1023
1024         child = new XMLNode ("priority");
1025         child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1026         root->add_child_nocopy (*child);
1027
1028         child = new XMLNode ("ports");
1029         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1030         root->add_child_nocopy (*child);
1031
1032         child = new XMLNode ("inchannels");
1033         child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1034         root->add_child_nocopy (*child);
1035
1036         child = new XMLNode ("outchannels");
1037         child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1038         root->add_child_nocopy (*child);
1039
1040         child = new XMLNode ("inlatency");
1041         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1042         root->add_child_nocopy (*child);
1043
1044         child = new XMLNode ("outlatency");
1045         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1046         root->add_child_nocopy (*child);
1047
1048         child = new XMLNode ("realtime");
1049         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1050         root->add_child_nocopy (*child);
1051
1052         child = new XMLNode ("nomemorylock");
1053         child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1054         root->add_child_nocopy (*child);
1055
1056         child = new XMLNode ("unlockmemory");
1057         child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1058         root->add_child_nocopy (*child);
1059
1060         child = new XMLNode ("softmode");
1061         child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1062         root->add_child_nocopy (*child);
1063
1064         child = new XMLNode ("force16bit");
1065         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1066         root->add_child_nocopy (*child);
1067
1068         child = new XMLNode ("hwmonitor");
1069         child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1070         root->add_child_nocopy (*child);
1071
1072         child = new XMLNode ("hwmeter");
1073         child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1074         root->add_child_nocopy (*child);
1075
1076         child = new XMLNode ("verbose");
1077         child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1078         root->add_child_nocopy (*child);
1079
1080         child = new XMLNode ("samplerate");
1081         child->add_property ("val", sample_rate_combo.get_active_text());
1082         root->add_child_nocopy (*child);
1083
1084         child = new XMLNode ("periodsize");
1085         child->add_property ("val", period_size_combo.get_active_text());
1086         root->add_child_nocopy (*child);
1087
1088         child = new XMLNode ("serverpath");
1089         child->add_property ("val", serverpath_combo.get_active_text());
1090         root->add_child_nocopy (*child);
1091
1092         child = new XMLNode ("driver");
1093         child->add_property ("val", driver_combo.get_active_text());
1094         root->add_child_nocopy (*child);
1095
1096         child = new XMLNode ("interface");
1097         child->add_property ("val", interface_combo.get_active_text());
1098         root->add_child_nocopy (*child);
1099
1100         child = new XMLNode ("timeout");
1101         child->add_property ("val", timeout_combo.get_active_text());
1102         root->add_child_nocopy (*child);
1103
1104         child = new XMLNode ("dither");
1105         child->add_property ("val", dither_mode_combo.get_active_text());
1106         root->add_child_nocopy (*child);
1107
1108         child = new XMLNode ("audiomode");
1109         child->add_property ("val", audio_mode_combo.get_active_text());
1110         root->add_child_nocopy (*child);
1111
1112         child = new XMLNode ("inputdevice");
1113         child->add_property ("val", input_device_combo.get_active_text());
1114         root->add_child_nocopy (*child);
1115
1116         child = new XMLNode ("outputdevice");
1117         child->add_property ("val", output_device_combo.get_active_text());
1118         root->add_child_nocopy (*child);
1119         
1120         return *root;
1121 }
1122
1123 void
1124 EngineControl::set_state (const XMLNode& root)
1125 {
1126         XMLNodeList          clist;
1127         XMLNodeConstIterator citer;
1128         XMLNode* child;
1129         XMLProperty* prop = NULL;
1130         bool using_dummy = false;
1131         
1132         int val;
1133         string strval;
1134         
1135         if ( (child = root.child ("driver"))){
1136                 prop = child->property("val");
1137                 if (prop && (prop->value() == "Dummy") ) {
1138                         using_dummy = true;
1139                 }
1140         }
1141         
1142         clist = root.children();
1143
1144         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1145                 if ( prop && (prop->value() == "FFADO" ))
1146                                 continue;
1147                 child = *citer;
1148
1149                 prop = child->property ("val");
1150
1151                 if (!prop || prop->value().empty()) {
1152
1153                         if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1154                                 continue;
1155                         error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1156                         continue;
1157                 }
1158                 
1159                 strval = prop->value();
1160
1161                 /* adjustments/spinners */
1162
1163                 if (child->name() == "periods") {
1164                         val = atoi (strval);
1165                         periods_adjustment.set_value(val);
1166                 } else if (child->name() == "priority") {
1167                         val = atoi (strval);
1168                         priority_adjustment.set_value(val);
1169                 } else if (child->name() == "ports") {
1170                         val = atoi (strval);
1171                         ports_adjustment.set_value(val);
1172                 } else if (child->name() == "inchannels") {
1173                         val = atoi (strval);
1174                         input_channels.set_value(val);
1175                 } else if (child->name() == "outchannels") {
1176                         val = atoi (strval);
1177                         output_channels.set_value(val);
1178                 } else if (child->name() == "inlatency") {
1179                         val = atoi (strval);
1180                         input_latency.set_value(val);
1181                 } else if (child->name() == "outlatency") {
1182                         val = atoi (strval);
1183                         output_latency.set_value(val);
1184                 }
1185
1186                 /* buttons */
1187
1188                 else if (child->name() == "realtime") {
1189                         val = atoi (strval);
1190                         realtime_button.set_active(val);
1191                 } else if (child->name() == "nomemorylock") {
1192                         val = atoi (strval);
1193                         no_memory_lock_button.set_active(val);
1194                 } else if (child->name() == "unlockmemory") {
1195                         val = atoi (strval);
1196                         unlock_memory_button.set_active(val);
1197                 } else if (child->name() == "softmode") {
1198                         val = atoi (strval);
1199                         soft_mode_button.set_active(val);
1200                 } else if (child->name() == "force16bit") {
1201                         val = atoi (strval);
1202                         force16bit_button.set_active(val);
1203                 } else if (child->name() == "hwmonitor") {
1204                         val = atoi (strval);
1205                         hw_monitor_button.set_active(val);
1206                 } else if (child->name() == "hwmeter") {
1207                         val = atoi (strval);
1208                         hw_meter_button.set_active(val);
1209                 } else if (child->name() == "verbose") {
1210                         val = atoi (strval);
1211                         verbose_output_button.set_active(val);
1212                 }
1213
1214                 /* combos */
1215
1216                 else if (child->name() == "samplerate") {
1217                         sample_rate_combo.set_active_text(strval);
1218                 } else if (child->name() == "periodsize") {
1219                         period_size_combo.set_active_text(strval);
1220                 } else if (child->name() == "serverpath") {
1221                         /* do not allow us to use a server path that doesn't
1222                            exist on this system. this handles cases where
1223                            the user has an RC file listing a serverpath
1224                            from some other machine.
1225                         */
1226                         vector<string>::iterator x;
1227                         for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1228                                 if (*x == strval) {
1229                                         break;
1230                                 }
1231                         }
1232                         if (x != server_strings.end()) {
1233                                 serverpath_combo.set_active_text (strval);
1234                         } else {
1235                                 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1236                                                              strval)
1237                                         << endmsg;
1238                         }
1239                 } else if (child->name() == "driver") {
1240                         driver_combo.set_active_text(strval);
1241                 } else if (child->name() == "interface") {
1242                         interface_combo.set_active_text(strval);
1243                 } else if (child->name() == "timeout") {
1244                         timeout_combo.set_active_text(strval);
1245                 } else if (child->name() == "dither") {
1246                         dither_mode_combo.set_active_text(strval);
1247                 } else if (child->name() == "audiomode") {
1248                         audio_mode_combo.set_active_text(strval);
1249                 } else if (child->name() == "inputdevice") {
1250                         input_device_combo.set_active_text(strval);
1251                 } else if (child->name() == "outputdevice") {
1252                         output_device_combo.set_active_text(strval);
1253                 }
1254         }
1255 }