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