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