stop using paul's .jackdrc as the hard coded path
[ardour.git] / gtk2_ardour / engine_dialog.cc
1 #include <vector>
2 #include <cmath>
3 #include <fstream>
4
5 #include <glibmm.h>
6
7 #include <ardour/profile.h>
8 #include <jack/jack.h>
9
10 #include <gtkmm/stock.h>
11 #include <gtkmm2ext/utils.h>
12
13 #include <pbd/convert.h>
14 #include <pbd/error.h>
15
16 #include "engine_dialog.h"
17 #include "i18n.h"
18
19 using namespace std;
20 using namespace Gtk;
21 using namespace Gtkmm2ext;
22 using namespace PBD;
23 using namespace Glib;
24
25 EngineControl::EngineControl ()
26         : periods_adjustment (2, 2, 16, 1, 2),
27           periods_spinner (periods_adjustment),
28           priority_adjustment (60, 10, 90, 1, 10),
29           priority_spinner (priority_adjustment),
30           ports_adjustment (128, 8, 1024, 1, 16),
31           ports_spinner (ports_adjustment),
32           realtime_button (_("Realtime")),
33           no_memory_lock_button (_("Do not lock memory")),
34           unlock_memory_button (_("Unlock memory")),
35           soft_mode_button (_("No zombies")),
36           monitor_button (_("Provide monitor ports")),
37           force16bit_button (_("Force 16 bit")),
38           hw_monitor_button (_("H/W monitoring")),
39           hw_meter_button (_("H/W metering")),
40           verbose_output_button (_("Verbose output")),
41           start_button (_("Start")),
42           stop_button (_("Stop")),
43           basic_packer (8, 2),
44           options_packer (12, 2),
45           device_packer (3, 2)
46 {
47         using namespace Notebook_Helpers;
48         Label* label;
49         vector<string> strings;
50
51         strings.push_back (_("8000Hz"));
52         strings.push_back (_("22050Hz"));
53         strings.push_back (_("44100Hz"));
54         strings.push_back (_("48000Hz"));
55         strings.push_back (_("88200Hz"));
56         strings.push_back (_("96000Hz"));
57         strings.push_back (_("192000Hz"));
58         set_popdown_strings (sample_rate_combo, strings);
59         sample_rate_combo.set_active_text ("48000Hz");
60
61         strings.clear ();
62         strings.push_back ("32");
63         strings.push_back ("64");
64         strings.push_back ("128");
65         strings.push_back ("256");
66         strings.push_back ("512");
67         strings.push_back ("1024");
68         strings.push_back ("2048");
69         strings.push_back ("4096");
70         strings.push_back ("8192");
71         set_popdown_strings (period_size_combo, strings);
72         period_size_combo.set_active_text ("1024");
73
74         /* basic parameters */
75
76         basic_packer.set_spacings (6);
77
78         strings.clear ();
79 #ifndef __APPLE
80         strings.push_back (X_("ALSA"));
81         strings.push_back (X_("OSS"));
82         strings.push_back (X_("FFADO"));
83 #else
84         strings.push_back (X_("CoreAudio"));
85 #endif
86         strings.push_back (X_("NetJACK"));
87         strings.push_back (X_("Dummy"));
88         set_popdown_strings (driver_combo, strings);
89         driver_combo.set_active_text (strings.front());
90
91         /* figure out available devices and set up interface_combo */
92
93         enumerate_devices ();
94         driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
95         driver_changed ();
96
97         strings.clear ();
98         strings.push_back (_("Duplex"));
99         strings.push_back (_("Playback only"));
100         strings.push_back (_("Capture only"));
101         set_popdown_strings (audio_mode_combo, strings);
102         audio_mode_combo.set_active_text (strings.front());
103
104         audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
105         audio_mode_changed ();
106
107         label = manage (new Label (_("Driver")));
108         basic_packer.attach (*label, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0);
109         basic_packer.attach (driver_combo, 1, 2, 0, 1, FILL|EXPAND, (AttachOptions) 0);
110
111         label = manage (new Label (_("Interface")));
112         basic_packer.attach (*label, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0);
113         basic_packer.attach (interface_combo, 1, 2, 1, 2, FILL|EXPAND, (AttachOptions) 0);
114
115         label = manage (new Label (_("Sample Rate")));
116         basic_packer.attach (*label, 0, 1, 2, 3, FILL|EXPAND, (AttachOptions) 0);
117         basic_packer.attach (sample_rate_combo, 1, 2, 2, 3, FILL|EXPAND, (AttachOptions) 0);
118
119         label = manage (new Label (_("Buffer size")));
120         basic_packer.attach (*label, 0, 1, 3, 4, FILL|EXPAND, (AttachOptions) 0);
121         basic_packer.attach (period_size_combo, 1, 2, 3, 4, FILL|EXPAND, (AttachOptions) 0);
122
123         label = manage (new Label (_("Number of buffers")));
124         basic_packer.attach (*label, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0);
125         basic_packer.attach (periods_spinner, 1, 2, 4, 5, FILL|EXPAND, (AttachOptions) 0);
126         periods_spinner.set_value (2);
127
128         label = manage (new Label (_("Approximate latency")));
129         basic_packer.attach (*label, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0);
130         basic_packer.attach (latency_label, 1, 2, 5, 6, FILL|EXPAND, (AttachOptions) 0);
131
132         sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
133         periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
134         period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
135         redisplay_latency();
136
137         label = manage (new Label (_("Audio Mode")));
138         basic_packer.attach (*label, 0, 1, 6, 7, FILL|EXPAND, (AttachOptions) 0);
139         basic_packer.attach (audio_mode_combo, 1, 2, 6, 7, FILL|EXPAND, (AttachOptions) 0);
140
141         /* 
142
143         if (engine_running()) {
144                 start_button.set_sensitive (false);
145         } else {
146                 stop_button.set_sensitive (false);
147         }
148
149         start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
150         stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
151         */
152
153         button_box.pack_start (start_button, false, false);
154         button_box.pack_start (stop_button, false, false);
155
156         // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
157
158         /* options */
159
160         options_packer.attach (realtime_button, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0);
161         label = manage (new Label (_("Realtime Priority")));
162         options_packer.attach (*label, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0);
163         options_packer.attach (priority_spinner, 1, 2, 1, 2, FILL|EXPAND, (AttachOptions) 0);
164         priority_spinner.set_value (60);
165
166         realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
167         realtime_changed ();
168
169 #ifndef __APPLE
170         options_packer.attach (no_memory_lock_button, 0, 1, 2, 3, FILL|EXPAND, (AttachOptions) 0);
171         options_packer.attach (unlock_memory_button, 0, 1, 3, 4, FILL|EXPAND, (AttachOptions) 0);
172         options_packer.attach (soft_mode_button, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0);
173         options_packer.attach (monitor_button, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0);
174         options_packer.attach (force16bit_button, 0, 1, 6, 7, FILL|EXPAND, (AttachOptions) 0);
175         options_packer.attach (hw_monitor_button, 0, 1, 7, 8, FILL|EXPAND, (AttachOptions) 0);
176         options_packer.attach (hw_meter_button, 0, 1, 8, 9, FILL|EXPAND, (AttachOptions) 0);
177         options_packer.attach (verbose_output_button, 0, 1, 9, 10, FILL|EXPAND, (AttachOptions) 0);
178 #else 
179         options_packer.attach (verbose_output_button, 0, 1, 2, 3, FILL|EXPAND, (AttachOptions) 0);
180 #endif
181
182         strings.clear ();
183         strings.push_back (_("Ignore"));
184         strings.push_back ("500 msec");
185         strings.push_back ("1 sec");
186         strings.push_back ("2 sec");
187         strings.push_back ("10 sec");
188         set_popdown_strings (timeout_combo, strings);
189         timeout_combo.set_active_text (strings.front ());
190
191         label = manage (new Label (_("Client timeout")));
192         options_packer.attach (*label, 0, 1, 11, 12, (AttachOptions) 0, (AttachOptions) 0);
193         options_packer.attach (timeout_combo, 1, 2, 11, 12, FILL|EXPAND, AttachOptions(0));
194
195         label = manage (new Label (_("Number of ports")));
196         options_packer.attach (*label, 0, 1, 12, 13, (AttachOptions) 0, (AttachOptions) 0);
197         options_packer.attach (ports_spinner, 1, 2, 12, 13, FILL|EXPAND, AttachOptions(0));
198
199         strings.clear ();
200
201         find_jack_servers (strings);
202
203         if (strings.empty()) {
204                 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
205                 /*NOTREACHED*/
206         }
207         
208         set_popdown_strings (serverpath_combo, strings);
209         serverpath_combo.set_active_text (strings.front());
210
211         if (strings.size() > 1) {
212                 label = manage (new Label (_("Server:")));
213                 options_packer.attach (*label, 0, 1, 11, 12, (AttachOptions) 0, (AttachOptions) 0);
214                 options_packer.attach (serverpath_combo, 1, 2, 11, 12, FILL|EXPAND, (AttachOptions) 0);
215         }
216
217         /* device settings */
218
219         device_packer.set_spacings (6);
220
221         label = manage (new Label (_("Input device")));
222         device_packer.attach (*label, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0);
223         device_packer.attach (input_device_combo, 1, 2, 0, 1, FILL|EXPAND, (AttachOptions) 0);
224         label = manage (new Label (_("Output device")));
225         device_packer.attach (*label, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0);
226         device_packer.attach (output_device_combo, 1, 2, 1, 2, FILL|EXPAND, (AttachOptions) 0); 
227         label = manage (new Label (_("Input channels")));
228         device_packer.attach (*label, 0, 1, 2, 3, FILL|EXPAND, (AttachOptions) 0);
229         device_packer.attach (input_channels, 1, 2, 2, 3, FILL|EXPAND, (AttachOptions) 0);
230         label = manage (new Label (_("Output channels")));
231         device_packer.attach (*label, 0, 1, 3, 4, FILL|EXPAND, (AttachOptions) 0);
232         device_packer.attach (output_channels, 1, 2, 3, 4, FILL|EXPAND, (AttachOptions) 0);
233         label = manage (new Label (_("Input latency (samples)")));
234         device_packer.attach (*label, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0);
235         device_packer.attach (input_latency, 1, 2, 4, 5, FILL|EXPAND, (AttachOptions) 0);
236         label = manage (new Label (_("Output latency (samples)")));
237         device_packer.attach (*label, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0);
238         device_packer.attach (output_latency, 1, 2, 5, 6, FILL|EXPAND, (AttachOptions) 0);
239
240         notebook.pages().push_back (TabElem (basic_packer, _("Basics")));
241         notebook.pages().push_back (TabElem (options_packer, _("Options")));
242         notebook.pages().push_back (TabElem (device_packer, _("Device Parameters")));
243
244         set_border_width (12);
245         pack_start (notebook);
246
247 }
248
249 EngineControl::~EngineControl ()
250 {
251
252 }
253
254 void
255 EngineControl::build_command_line (vector<string>& cmd)
256 {
257         string str;
258         bool using_oss = false;
259         bool using_alsa = false;
260         bool using_coreaudio = false;
261         bool using_netjack = false;
262         bool using_ffado = false;
263
264         /* first, path to jackd */
265
266         cmd.push_back (serverpath_combo.get_active_text ());
267         
268         /* now jackd arguments */
269
270         str = timeout_combo.get_active_text ();
271         if (str != _("Ignore")) {
272                 double secs;
273                 uint32_t msecs;
274                 atof (str);
275                 msecs = (uint32_t) floor (secs * 1000.0);
276                 cmd.push_back ("-t");
277                 cmd.push_back (to_string (msecs, std::dec));
278         }
279
280         if (no_memory_lock_button.get_active()) {
281                 cmd.push_back ("-m"); /* no munlock */
282         }
283         
284         cmd.push_back ("-p"); /* port max */
285         cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
286
287         if (realtime_button.get_active()) {
288                 cmd.push_back ("-R");
289                 cmd.push_back ("-P");
290                 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
291         }
292
293         if (unlock_memory_button.get_active()) {
294                 cmd.push_back ("-u");
295         }
296
297         if (verbose_output_button.get_active()) {
298                 cmd.push_back ("-v");
299         }
300         
301         /* now add fixed arguments (not user-selectable) */
302
303         cmd.push_back ("-T"); // temporary */
304
305         /* next the driver */
306
307         cmd.push_back ("-d");
308
309         str = driver_combo.get_active_text ();
310         if (str == X_("ALSA")) {
311                 using_alsa = true;
312                 cmd.push_back ("alsa");
313         } else if (str == X_("OSS")) {
314                 using_oss = true;
315                 cmd.push_back ("oss");
316         } else if (str == X_("CoreAudio")) {
317                 using_coreaudio = true;
318                 cmd.push_back ("coreaudio");
319         } else if (str == X_("NetJACK")) {
320                 using_netjack = true;
321                 cmd.push_back ("netjack");
322         } else if (str == X_("FFADO")) {
323                 using_ffado = true;
324                 cmd.push_back ("ffado");
325         }
326
327         /* driver arguments */
328
329         str = audio_mode_combo.get_active_text();
330         if (str == _("Duplex")) {
331                 /* relax */
332         } else if (str == _("Playback only")) {
333                 cmd.push_back ("-P");
334         } else if (str == _("Capture only")) {
335                 cmd.push_back ("-C");
336         }
337
338         cmd.push_back ("-n");
339         cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
340
341         cmd.push_back ("-r");
342         cmd.push_back (to_string (get_rate(), std::dec));
343         
344         cmd.push_back ("-p");
345         cmd.push_back (period_size_combo.get_active_text());
346
347         if (using_alsa) {
348
349                 cmd.push_back ("-d");
350                 cmd.push_back (interface_combo.get_active_text());
351
352                 if (hw_meter_button.get_active()) {
353                         cmd.push_back ("-M");
354                 }
355                 
356                 if (hw_monitor_button.get_active()) {
357                         cmd.push_back ("-H");
358                 }
359
360                 str = dither_mode_combo.get_active_text();
361                 if (str == _("None")) {
362                 } else if (str == _("Triangular")) {
363                         cmd.push_back ("-z triangular");
364                 } else if (str == _("Rectangular")) {
365                         cmd.push_back ("-z rectangular");
366                 } else if (str == _("Shaped")) {
367                         cmd.push_back ("-z shaped");
368                 }
369
370                 if (force16bit_button.get_active()) {
371                         cmd.push_back ("-S");
372                 }
373                 
374                 if (soft_mode_button.get_active()) {
375                         cmd.push_back ("-s");
376                 }
377
378         } else if (using_coreaudio) {
379
380                 cmd.push_back ("-I");
381                 cmd.push_back (interface_combo.get_active_text());
382
383         } else if (using_oss) {
384
385         } else if (using_netjack) {
386
387         }
388 }
389
390 bool
391 EngineControl::engine_running ()
392 {
393         jack_status_t status;
394         jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
395
396         if (status == 0) {
397                 jack_client_close (c);
398                 return true;
399         }
400         return false;
401 }
402
403 int
404 EngineControl::start_engine ()
405 {
406         vector<string> args;
407         std::string cwd = "/tmp";
408         int ret = 0;
409
410         build_command_line (args);
411
412         Glib::ustring jackdrc_path = Glib::get_home_dir();
413         jackdrc_path += "/.jackdrc";
414
415         ofstream jackdrc (jackdrc_path.c_str());
416         if (!jackdrc) {
417                 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
418                 return -1;
419         }
420
421         for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
422                 jackdrc << (*i) << ' ';
423         }
424         jackdrc << endl;
425         jackdrc.close ();
426         
427 #if 0
428
429         try {
430                 spawn_async_with_pipes (cwd, args, SpawnFlags (0), sigc::slot<void>(), &engine_pid, &engine_stdin, &engine_stdout, &engine_stderr);
431         }
432         
433         catch (Glib::Exception& err) {
434                 error << _("could not start JACK server: ") << err.what() << endmsg;
435                 ret = -1;
436         }
437 #endif
438
439         return ret;
440 }
441
442 int
443 EngineControl::stop_engine ()
444 {
445         close (engine_stdin);
446         close (engine_stderr);
447         close (engine_stdout);
448         spawn_close_pid (engine_pid);
449         return 0;
450 }
451
452 void
453 EngineControl::realtime_changed ()
454 {
455         priority_spinner.set_sensitive (realtime_button.get_active());
456 }
457
458 void
459 EngineControl::enumerate_devices ()
460 {
461         /* note: case matters for the map keys */
462
463 #ifdef __APPLE
464         devices["CoreAudio"] = enumerate_coreaudio_devices ();
465 #else
466         devices["ALSA"] = enumerate_alsa_devices ();
467         devices["FFADO"] = enumerate_ffado_devices ();
468         devices["OSS"] = enumerate_oss_devices ();
469         devices["Dummy"] = enumerate_dummy_devices ();
470         devices["NetJACK"] = enumerate_netjack_devices ();
471 #endif
472 }
473
474 #ifdef __APPLE
475 vector<string>
476 EngineControl::enumerate_coreaudio_devices ()
477 {
478         vector<string> devs;
479         return devs;
480 }
481 #else
482 vector<string>
483 EngineControl::enumerate_alsa_devices ()
484 {
485         vector<string> devs;
486         devs.push_back ("hw:0");
487         devs.push_back ("hw:1");
488         devs.push_back ("plughw:0");
489         devs.push_back ("plughw:1");
490         return devs;
491 }
492 vector<string>
493 EngineControl::enumerate_ffado_devices ()
494 {
495         vector<string> devs;
496         return devs;
497 }
498 vector<string>
499 EngineControl::enumerate_oss_devices ()
500 {
501         vector<string> devs;
502         return devs;
503 }
504 vector<string>
505 EngineControl::enumerate_dummy_devices ()
506 {
507         vector<string> devs;
508         return devs;
509 }
510 vector<string>
511 EngineControl::enumerate_netjack_devices ()
512 {
513         vector<string> devs;
514         return devs;
515 }
516 #endif
517
518 void
519 EngineControl::driver_changed ()
520 {
521         string driver = driver_combo.get_active_text();
522         vector<string>& strings = devices[driver];
523         
524         set_popdown_strings (interface_combo, strings);
525
526         if (!strings.empty()) {
527                 interface_combo.set_active_text (strings.front());
528         }
529         
530         if (driver == "ALSA") {
531                 soft_mode_button.set_sensitive (true);
532                 force16bit_button.set_sensitive (true);
533                 hw_monitor_button.set_sensitive (true);
534                 hw_meter_button.set_sensitive (true);
535                 monitor_button.set_sensitive (true);
536         } else {
537                 soft_mode_button.set_sensitive (false);
538                 force16bit_button.set_sensitive (false);
539                 hw_monitor_button.set_sensitive (false);
540                 hw_meter_button.set_sensitive (false);
541                 monitor_button.set_sensitive (false);
542         }
543 }
544
545 uint32_t
546 EngineControl::get_rate ()
547 {
548         return atoi (sample_rate_combo.get_active_text ());
549 }
550
551 void
552 EngineControl::redisplay_latency ()
553 {
554         uint32_t rate = get_rate();
555         float periods = periods_adjustment.get_value();
556         float period_size = atof (period_size_combo.get_active_text());
557
558         char buf[32];
559         snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
560
561         latency_label.set_text (buf);
562 }
563
564 void
565 EngineControl::audio_mode_changed ()
566 {
567         Glib::ustring str = audio_mode_combo.get_active_text();
568
569         if (str == _("Duplex")) {
570                 input_device_combo.set_sensitive (false);
571                 output_device_combo.set_sensitive (false);
572         } else {
573                 input_device_combo.set_sensitive (true);
574                 output_device_combo.set_sensitive (true);
575         }
576 }
577
578 void
579 EngineControl::find_jack_servers (vector<string>& strings)
580 {
581 #ifdef __APPLE
582         if (Profile->get_single_package()) {
583
584                 /* this magic lets us finds the path to the OSX bundle, and then
585                    we infer JACK's location from there
586                 */
587                 
588                 CFURLRef pluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
589                 CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef, 
590                                                               kCFURLPOSIXPathStyle);
591                 std::string path = CFStringGetCStringPtr(macPath, 
592                                                          CFStringGetSystemEncoding());
593                 CFRelease(pluginRef);
594                 CFRelease(macPath);
595                 
596                 path += '/jackd';
597
598                 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
599                         strings.push_back ();
600                 } else {
601                         warning << _("JACK appears to be missing from the Ardour bundle") << endmsg;
602                 }
603 #endif
604
605         if (Glib::file_test ("/usr/bin/jackd", FILE_TEST_EXISTS)) {
606                 strings.push_back ("/usr/bin/jackd");
607         }
608         if (Glib::file_test ("/usr/local/bin/jackd", FILE_TEST_EXISTS)) {
609                 strings.push_back ("/usr/local/bin/jackd");
610         }
611         if (Glib::file_test ("/opt/bin/jackd", FILE_TEST_EXISTS)) {
612                 strings.push_back ("/opt/bin/jackd");
613         }
614         if (Glib::file_test ("/usr/bin/jackdmp", FILE_TEST_EXISTS)) {
615                 strings.push_back ("/usr/bin/jackd");
616         }
617         if (Glib::file_test ("/usr/local/bin/jackdmp", FILE_TEST_EXISTS)) {
618                 strings.push_back ("/usr/local/bin/jackd");
619         }
620         if (Glib::file_test ("/opt/bin/jackdmp", FILE_TEST_EXISTS)) {
621                 strings.push_back ("/opt/bin/jackd");
622         }
623
624 }