9 #include <gtkmm/stock.h>
10 #include <gtkmm2ext/utils.h>
12 #include <pbd/convert.h>
13 #include <pbd/error.h>
15 #include "engine_dialog.h"
20 using namespace Gtkmm2ext;
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")),
43 options_packer (12, 2),
46 using namespace Notebook_Helpers;
48 vector<string> strings;
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");
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");
73 /* basic parameters */
75 basic_packer.set_spacings (6);
79 strings.push_back (X_("ALSA"));
80 strings.push_back (X_("OSS"));
81 strings.push_back (X_("FFADO"));
83 strings.push_back (X_("CoreAudio"));
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());
90 /* figure out available devices and set up interface_combo */
93 driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
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());
103 audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
104 audio_mode_changed ();
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);
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);
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);
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);
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);
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);
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));
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);
142 if (engine_running()) {
143 start_button.set_sensitive (false);
145 stop_button.set_sensitive (false);
148 start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
149 stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
152 button_box.pack_start (start_button, false, false);
153 button_box.pack_start (stop_button, false, false);
155 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
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);
165 realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
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);
178 options_packer.attach (verbose_output_button, 0, 1, 2, 3, FILL|EXPAND, (AttachOptions) 0);
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 ());
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));
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));
200 if (Glib::file_test ("/usr/bin/jackd", FILE_TEST_EXISTS)) {
201 strings.push_back ("/usr/bin/jackd");
203 if (Glib::file_test ("/usr/local/bin/jackd", FILE_TEST_EXISTS)) {
204 strings.push_back ("/usr/local/bin/jackd");
206 if (Glib::file_test ("/opt/bin/jackd", FILE_TEST_EXISTS)) {
207 strings.push_back ("/opt/bin/jackd");
209 if (Glib::file_test ("/usr/bin/jackdmp", FILE_TEST_EXISTS)) {
210 strings.push_back ("/usr/bin/jackd");
212 if (Glib::file_test ("/usr/local/bin/jackdmp", FILE_TEST_EXISTS)) {
213 strings.push_back ("/usr/local/bin/jackd");
215 if (Glib::file_test ("/opt/bin/jackdmp", FILE_TEST_EXISTS)) {
216 strings.push_back ("/opt/bin/jackd");
219 if (strings.empty()) {
220 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
224 set_popdown_strings (serverpath_combo, strings);
225 serverpath_combo.set_active_text (strings.front());
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);
233 /* device settings */
235 device_packer.set_spacings (6);
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);
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")));
260 set_border_width (12);
261 pack_start (notebook);
265 EngineControl::~EngineControl ()
271 EngineControl::build_command_line (vector<string>& cmd)
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;
280 /* first, path to jackd */
282 cmd.push_back (serverpath_combo.get_active_text ());
284 /* now jackd arguments */
286 str = timeout_combo.get_active_text ();
287 if (str != _("Ignore")) {
291 msecs = (uint32_t) floor (secs * 1000.0);
292 cmd.push_back ("-t");
293 cmd.push_back (to_string (msecs, std::dec));
296 if (no_memory_lock_button.get_active()) {
297 cmd.push_back ("-m"); /* no munlock */
300 cmd.push_back ("-p"); /* port max */
301 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
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));
309 if (unlock_memory_button.get_active()) {
310 cmd.push_back ("-u");
313 if (verbose_output_button.get_active()) {
314 cmd.push_back ("-v");
317 /* now add fixed arguments (not user-selectable) */
319 cmd.push_back ("-T"); // temporary */
321 /* next the driver */
323 cmd.push_back ("-d");
325 str = driver_combo.get_active_text ();
326 if (str == X_("ALSA")) {
328 cmd.push_back ("alsa");
329 } else if (str == X_("OSS")) {
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")) {
340 cmd.push_back ("ffado");
343 /* driver arguments */
345 str = audio_mode_combo.get_active_text();
346 if (str == _("Duplex")) {
348 } else if (str == _("Playback only")) {
349 cmd.push_back ("-P");
350 } else if (str == _("Capture only")) {
351 cmd.push_back ("-C");
354 cmd.push_back ("-n");
355 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
357 cmd.push_back ("-r");
358 cmd.push_back (to_string (get_rate(), std::dec));
360 cmd.push_back ("-p");
361 cmd.push_back (period_size_combo.get_active_text());
365 cmd.push_back ("-d");
366 cmd.push_back (interface_combo.get_active_text());
368 if (hw_meter_button.get_active()) {
369 cmd.push_back ("-M");
372 if (hw_monitor_button.get_active()) {
373 cmd.push_back ("-H");
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");
386 if (force16bit_button.get_active()) {
387 cmd.push_back ("-S");
390 if (soft_mode_button.get_active()) {
391 cmd.push_back ("-s");
394 } else if (using_coreaudio) {
396 cmd.push_back ("-I");
397 cmd.push_back (interface_combo.get_active_text());
399 } else if (using_oss) {
401 } else if (using_netjack) {
407 EngineControl::engine_running ()
409 jack_status_t status;
410 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
413 jack_client_close (c);
420 EngineControl::start_engine ()
423 std::string cwd = "/tmp";
426 build_command_line (args);
428 ofstream jackdrc ("/home/paul/.jackdrc");
430 error << _("cannot open JACK rc file to store parameters") << endmsg;
434 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
435 jackdrc << (*i) << ' ';
443 spawn_async_with_pipes (cwd, args, SpawnFlags (0), sigc::slot<void>(), &engine_pid, &engine_stdin, &engine_stdout, &engine_stderr);
446 catch (Glib::Exception& err) {
447 error << _("could not start JACK server: ") << err.what() << endmsg;
456 EngineControl::stop_engine ()
458 close (engine_stdin);
459 close (engine_stderr);
460 close (engine_stdout);
461 spawn_close_pid (engine_pid);
466 EngineControl::realtime_changed ()
468 priority_spinner.set_sensitive (realtime_button.get_active());
472 EngineControl::enumerate_devices ()
474 /* note: case matters for the map keys */
477 devices["CoreAudio"] = enumerate_coreaudio_devices ();
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 ();
489 EngineControl::enumerate_coreaudio_devices ()
496 EngineControl::enumerate_alsa_devices ()
499 devs.push_back ("hw:0");
500 devs.push_back ("hw:1");
501 devs.push_back ("plughw:0");
502 devs.push_back ("plughw:1");
506 EngineControl::enumerate_ffado_devices ()
512 EngineControl::enumerate_oss_devices ()
518 EngineControl::enumerate_dummy_devices ()
524 EngineControl::enumerate_netjack_devices ()
532 EngineControl::driver_changed ()
534 string driver = driver_combo.get_active_text();
535 vector<string>& strings = devices[driver];
537 set_popdown_strings (interface_combo, strings);
539 if (!strings.empty()) {
540 interface_combo.set_active_text (strings.front());
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);
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);
559 EngineControl::get_rate ()
561 return atoi (sample_rate_combo.get_active_text ());
565 EngineControl::redisplay_latency ()
567 uint32_t rate = get_rate();
568 float periods = periods_adjustment.get_value();
569 float period_size = atof (period_size_combo.get_active_text());
572 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
574 latency_label.set_text (buf);
578 EngineControl::audio_mode_changed ()
580 Glib::ustring str = audio_mode_combo.get_active_text();
582 if (str == _("Duplex")) {
583 input_device_combo.set_sensitive (false);
584 output_device_combo.set_sensitive (false);
586 input_device_combo.set_sensitive (true);
587 output_device_combo.set_sensitive (true);