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