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