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