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