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