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