none of this was needed, so its not so dirty after all :)
[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 ("firewire");
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         backend_devs.clear ();
761         return devs;
762 }
763 vector<string>
764 EngineControl::enumerate_oss_devices ()
765 {
766         vector<string> devs;
767         return devs;
768 }
769 vector<string>
770 EngineControl::enumerate_dummy_devices ()
771 {
772         vector<string> devs;
773         return devs;
774 }
775 vector<string>
776 EngineControl::enumerate_netjack_devices ()
777 {
778         vector<string> devs;
779         return devs;
780 }
781 #endif
782
783 void
784 EngineControl::driver_changed ()
785 {
786         string driver = driver_combo.get_active_text();
787         vector<string>& strings = devices[driver];
788         string::size_type maxlen = 0;
789         int maxindex = -1;
790         int n = 0;
791
792         for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
793                 if ((*i).length() > maxlen) {
794                         maxlen = (*i).length();
795                         maxindex = n;
796                 }
797         }
798
799         set_popdown_strings (interface_combo, strings);
800         set_popdown_strings (input_device_combo, strings);
801         set_popdown_strings (output_device_combo, strings);
802
803         if (!strings.empty()) {
804                 interface_combo.set_active_text (strings.front());
805                 input_device_combo.set_active_text (strings.front());
806                 output_device_combo.set_active_text (strings.front());
807         }
808         
809         if (driver == "ALSA") {
810                 soft_mode_button.set_sensitive (true);
811                 force16bit_button.set_sensitive (true);
812                 hw_monitor_button.set_sensitive (true);
813                 hw_meter_button.set_sensitive (true);
814                 monitor_button.set_sensitive (true);
815         } else {
816                 soft_mode_button.set_sensitive (false);
817                 force16bit_button.set_sensitive (false);
818                 hw_monitor_button.set_sensitive (false);
819                 hw_meter_button.set_sensitive (false);
820                 monitor_button.set_sensitive (false);
821         }
822 }
823
824 uint32_t
825 EngineControl::get_rate ()
826 {
827         return atoi (sample_rate_combo.get_active_text ());
828 }
829
830 void
831 EngineControl::redisplay_latency ()
832 {
833         uint32_t rate = get_rate();
834 #ifdef __APPLE_
835         float periods = 2;
836 #else
837         float periods = periods_adjustment.get_value();
838 #endif
839         float period_size = atof (period_size_combo.get_active_text());
840
841         char buf[32];
842         snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
843
844         latency_label.set_text (buf);
845 }
846
847 void
848 EngineControl::audio_mode_changed ()
849 {
850         Glib::ustring str = audio_mode_combo.get_active_text();
851
852         if (str == _("Playback/Recording on 1 Device")) {
853                 input_device_combo.set_sensitive (false);
854                 output_device_combo.set_sensitive (false);
855         } else if (str == _("Playback/Recording on 2 Devices")) {
856                 input_device_combo.set_sensitive (true);
857                 output_device_combo.set_sensitive (true);
858         } else if (str == _("Playback only")) {
859                 output_device_combo.set_sensitive (true);
860         } else if (str == _("Recording only")) {
861                 input_device_combo.set_sensitive (true);
862         }
863 }
864
865 static bool jack_server_filter(const string& str, void *arg)
866 {
867    return str == "jackd" || str == "jackdmp";
868 }
869
870 void
871 EngineControl::find_jack_servers (vector<string>& strings)
872 {
873 #ifdef __APPLE__
874         /* this magic lets us finds the path to the OSX bundle, and then
875            we infer JACK's location from there
876         */
877         
878         char execpath[MAXPATHLEN+1];
879         uint32_t pathsz = sizeof (execpath);
880
881         _NSGetExecutablePath (execpath, &pathsz);
882         
883         Glib::ustring path (Glib::path_get_dirname (execpath));
884         path += "/jackd";
885
886         if (Glib::file_test (path, FILE_TEST_EXISTS)) {
887                 strings.push_back (path);
888         } 
889
890         if (getenv ("ARDOUR_WITH_JACK")) {
891                 /* no other options - only use the JACK we supply */
892                 if (strings.empty()) {
893                         fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
894                         /*NOTREACHED*/
895                 }
896                 return;
897         }
898 #else
899         string path;
900 #endif
901         
902         PathScanner scanner;
903         vector<string *> *jack_servers;
904         std::map<string,int> un;
905         
906         path = getenv ("PATH");
907
908         jack_servers = scanner (path, jack_server_filter, 0, false, true);
909         
910         vector<string *>::iterator iter;
911         
912         for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
913                 string p = **iter;
914                 
915                 if (un[p]++ == 0) {
916                         strings.push_back(p);
917                 }
918         }
919 }
920
921 string
922 EngineControl::get_device_name (const string& driver, const string& human_readable)
923 {
924         vector<string>::iterator n;
925         vector<string>::iterator i;
926
927         if (backend_devs.empty()) {
928                 return human_readable;
929         }
930         
931         for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
932                 if (human_readable == (*i)) {
933                         return (*n);
934                 }
935         }
936         
937         if (i == devices[driver].end()) {
938                 fatal << string_compose (_("programming error: %1"), "true hardware name for ID missing") << endmsg;
939                 /*NOTREACHED*/
940         }
941
942         /* keep gcc happy */
943
944         return string();
945 }
946
947 XMLNode&
948 EngineControl::get_state ()
949 {
950         XMLNode* root = new XMLNode ("AudioSetup");
951         XMLNode* child;
952         Glib::ustring path;
953
954         child = new XMLNode ("periods");
955         child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
956         root->add_child_nocopy (*child);
957
958         child = new XMLNode ("priority");
959         child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
960         root->add_child_nocopy (*child);
961
962         child = new XMLNode ("ports");
963         child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
964         root->add_child_nocopy (*child);
965
966         child = new XMLNode ("inchannels");
967         child->add_property ("val", to_string (input_channels.get_value(), std::dec));
968         root->add_child_nocopy (*child);
969
970         child = new XMLNode ("outchannels");
971         child->add_property ("val", to_string (output_channels.get_value(), std::dec));
972         root->add_child_nocopy (*child);
973
974         child = new XMLNode ("inlatency");
975         child->add_property ("val", to_string (input_latency.get_value(), std::dec));
976         root->add_child_nocopy (*child);
977
978         child = new XMLNode ("outlatency");
979         child->add_property ("val", to_string (output_latency.get_value(), std::dec));
980         root->add_child_nocopy (*child);
981
982         child = new XMLNode ("realtime");
983         child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
984         root->add_child_nocopy (*child);
985
986         child = new XMLNode ("nomemorylock");
987         child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
988         root->add_child_nocopy (*child);
989
990         child = new XMLNode ("unlockmemory");
991         child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
992         root->add_child_nocopy (*child);
993
994         child = new XMLNode ("softmode");
995         child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
996         root->add_child_nocopy (*child);
997
998         child = new XMLNode ("force16bit");
999         child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1000         root->add_child_nocopy (*child);
1001
1002         child = new XMLNode ("hwmonitor");
1003         child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1004         root->add_child_nocopy (*child);
1005
1006         child = new XMLNode ("hwmeter");
1007         child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1008         root->add_child_nocopy (*child);
1009
1010         child = new XMLNode ("verbose");
1011         child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1012         root->add_child_nocopy (*child);
1013
1014         child = new XMLNode ("samplerate");
1015         child->add_property ("val", sample_rate_combo.get_active_text());
1016         root->add_child_nocopy (*child);
1017
1018         child = new XMLNode ("periodsize");
1019         child->add_property ("val", period_size_combo.get_active_text());
1020         root->add_child_nocopy (*child);
1021
1022         child = new XMLNode ("serverpath");
1023         child->add_property ("val", serverpath_combo.get_active_text());
1024         root->add_child_nocopy (*child);
1025
1026         child = new XMLNode ("driver");
1027         child->add_property ("val", driver_combo.get_active_text());
1028         root->add_child_nocopy (*child);
1029
1030         child = new XMLNode ("interface");
1031         child->add_property ("val", interface_combo.get_active_text());
1032         root->add_child_nocopy (*child);
1033
1034         child = new XMLNode ("timeout");
1035         child->add_property ("val", timeout_combo.get_active_text());
1036         root->add_child_nocopy (*child);
1037
1038         child = new XMLNode ("dither");
1039         child->add_property ("val", dither_mode_combo.get_active_text());
1040         root->add_child_nocopy (*child);
1041
1042         child = new XMLNode ("audiomode");
1043         child->add_property ("val", audio_mode_combo.get_active_text());
1044         root->add_child_nocopy (*child);
1045
1046         child = new XMLNode ("inputdevice");
1047         child->add_property ("val", input_device_combo.get_active_text());
1048         root->add_child_nocopy (*child);
1049
1050         child = new XMLNode ("outputdevice");
1051         child->add_property ("val", output_device_combo.get_active_text());
1052         root->add_child_nocopy (*child);
1053         
1054         return *root;
1055 }
1056
1057 void
1058 EngineControl::set_state (const XMLNode& root)
1059 {
1060         XMLNodeList          clist;
1061         XMLNodeConstIterator citer;
1062         XMLNode* child;
1063         XMLProperty* prop;
1064
1065    bool using_dummy = false;
1066
1067         int val;
1068         string strval;
1069
1070    if ( (child = root.child("driver"))){
1071       prop = child->property("val");
1072       if (prop && (prop->value() == "Dummy") ) {
1073          using_dummy = true;
1074       }
1075    }
1076
1077         clist = root.children();
1078
1079         for (citer = clist.begin(); citer != clist.end(); ++citer) {
1080
1081                 child = *citer;
1082
1083                 prop = child->property ("val");
1084
1085                 if (!prop || prop->value().empty()) {
1086          if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1087                continue;
1088                         error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1089                         continue;
1090                 }
1091                 
1092                 strval = prop->value();
1093
1094                 /* adjustments/spinners */
1095
1096                 if (child->name() == "periods") {
1097                         val = atoi (strval);
1098                         periods_adjustment.set_value(val);
1099                 } else if (child->name() == "priority") {
1100                         val = atoi (strval);
1101                         priority_adjustment.set_value(val);
1102                 } else if (child->name() == "ports") {
1103                         val = atoi (strval);
1104                         ports_adjustment.set_value(val);
1105                 } else if (child->name() == "inchannels") {
1106                         val = atoi (strval);
1107                         input_channels.set_value(val);
1108                 } else if (child->name() == "outchannels") {
1109                         val = atoi (strval);
1110                         output_channels.set_value(val);
1111                 } else if (child->name() == "inlatency") {
1112                         val = atoi (strval);
1113                         input_latency.set_value(val);
1114                 } else if (child->name() == "outlatency") {
1115                         val = atoi (strval);
1116                         output_latency.set_value(val);
1117                 }
1118
1119                 /* buttons */
1120
1121                 else if (child->name() == "realtime") {
1122                         val = atoi (strval);
1123                         realtime_button.set_active(val);
1124                 } else if (child->name() == "nomemorylock") {
1125                         val = atoi (strval);
1126                         no_memory_lock_button.set_active(val);
1127                 } else if (child->name() == "unlockmemory") {
1128                         val = atoi (strval);
1129                         unlock_memory_button.set_active(val);
1130                 } else if (child->name() == "softmode") {
1131                         val = atoi (strval);
1132                         soft_mode_button.set_active(val);
1133                 } else if (child->name() == "force16bit") {
1134                         val = atoi (strval);
1135                         force16bit_button.set_active(val);
1136                 } else if (child->name() == "hwmonitor") {
1137                         val = atoi (strval);
1138                         hw_monitor_button.set_active(val);
1139                 } else if (child->name() == "hwmeter") {
1140                         val = atoi (strval);
1141                         hw_meter_button.set_active(val);
1142                 } else if (child->name() == "verbose") {
1143                         val = atoi (strval);
1144                         verbose_output_button.set_active(val);
1145                 }
1146
1147                 /* combos */
1148
1149                 else if (child->name() == "samplerate") {
1150                         sample_rate_combo.set_active_text(strval);
1151                 } else if (child->name() == "periodsize") {
1152                         period_size_combo.set_active_text(strval);
1153                 } else if (child->name() == "serverpath") {
1154                         /* do not allow us to use a server path that doesn't
1155                            exist on this system. this handles cases where
1156                            the user has an RC file listing a serverpath
1157                            from some other machine.
1158                         */
1159                         vector<string>::iterator x;
1160                         for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1161                                 if (*x == strval) {
1162                                         break;
1163                                 }
1164                         }
1165                         if (x != server_strings.end()) {
1166                                 serverpath_combo.set_active_text (strval);
1167                         } else {
1168                                 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1169                                                              strval)
1170                                         << endmsg;
1171                         }
1172                 } else if (child->name() == "driver") {
1173                         driver_combo.set_active_text(strval);
1174                 } else if (child->name() == "interface") {
1175                         interface_combo.set_active_text(strval);
1176                 } else if (child->name() == "timeout") {
1177                         timeout_combo.set_active_text(strval);
1178                 } else if (child->name() == "dither") {
1179                         dither_mode_combo.set_active_text(strval);
1180                 } else if (child->name() == "audiomode") {
1181                         audio_mode_combo.set_active_text(strval);
1182                 } else if (child->name() == "inputdevice") {
1183                         input_device_combo.set_active_text(strval);
1184                 } else if (child->name() == "outputdevice") {
1185                         output_device_combo.set_active_text(strval);
1186                 }
1187         }
1188 }