7 #include <ardour/profile.h>
10 #include <gtkmm/stock.h>
11 #include <gtkmm2ext/utils.h>
13 #include <pbd/convert.h>
14 #include <pbd/error.h>
16 #include "engine_dialog.h"
21 using namespace Gtkmm2ext;
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")),
44 options_packer (12, 2),
47 using namespace Notebook_Helpers;
49 vector<string> strings;
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");
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");
74 /* basic parameters */
76 basic_packer.set_spacings (6);
80 strings.push_back (X_("ALSA"));
81 strings.push_back (X_("OSS"));
82 strings.push_back (X_("FFADO"));
84 strings.push_back (X_("CoreAudio"));
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());
91 /* figure out available devices and set up interface_combo */
94 driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
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());
104 audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
105 audio_mode_changed ();
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);
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);
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);
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);
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);
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);
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));
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);
143 if (engine_running()) {
144 start_button.set_sensitive (false);
146 stop_button.set_sensitive (false);
149 start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
150 stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
153 button_box.pack_start (start_button, false, false);
154 button_box.pack_start (stop_button, false, false);
156 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
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);
166 realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
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);
179 options_packer.attach (verbose_output_button, 0, 1, 2, 3, FILL|EXPAND, (AttachOptions) 0);
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 ());
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));
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));
201 find_jack_servers (strings);
203 if (strings.empty()) {
204 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
208 set_popdown_strings (serverpath_combo, strings);
209 serverpath_combo.set_active_text (strings.front());
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);
217 /* device settings */
219 device_packer.set_spacings (6);
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);
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")));
244 set_border_width (12);
245 pack_start (notebook);
249 EngineControl::~EngineControl ()
255 EngineControl::build_command_line (vector<string>& cmd)
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;
264 /* first, path to jackd */
266 cmd.push_back (serverpath_combo.get_active_text ());
268 /* now jackd arguments */
270 str = timeout_combo.get_active_text ();
271 if (str != _("Ignore")) {
275 msecs = (uint32_t) floor (secs * 1000.0);
276 cmd.push_back ("-t");
277 cmd.push_back (to_string (msecs, std::dec));
280 if (no_memory_lock_button.get_active()) {
281 cmd.push_back ("-m"); /* no munlock */
284 cmd.push_back ("-p"); /* port max */
285 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
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));
293 if (unlock_memory_button.get_active()) {
294 cmd.push_back ("-u");
297 if (verbose_output_button.get_active()) {
298 cmd.push_back ("-v");
301 /* now add fixed arguments (not user-selectable) */
303 cmd.push_back ("-T"); // temporary */
305 /* next the driver */
307 cmd.push_back ("-d");
309 str = driver_combo.get_active_text ();
310 if (str == X_("ALSA")) {
312 cmd.push_back ("alsa");
313 } else if (str == X_("OSS")) {
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")) {
324 cmd.push_back ("ffado");
327 /* driver arguments */
329 str = audio_mode_combo.get_active_text();
330 if (str == _("Duplex")) {
332 } else if (str == _("Playback only")) {
333 cmd.push_back ("-P");
334 } else if (str == _("Capture only")) {
335 cmd.push_back ("-C");
338 cmd.push_back ("-n");
339 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
341 cmd.push_back ("-r");
342 cmd.push_back (to_string (get_rate(), std::dec));
344 cmd.push_back ("-p");
345 cmd.push_back (period_size_combo.get_active_text());
349 cmd.push_back ("-d");
350 cmd.push_back (interface_combo.get_active_text());
352 if (hw_meter_button.get_active()) {
353 cmd.push_back ("-M");
356 if (hw_monitor_button.get_active()) {
357 cmd.push_back ("-H");
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");
370 if (force16bit_button.get_active()) {
371 cmd.push_back ("-S");
374 if (soft_mode_button.get_active()) {
375 cmd.push_back ("-s");
378 } else if (using_coreaudio) {
380 cmd.push_back ("-I");
381 cmd.push_back (interface_combo.get_active_text());
383 } else if (using_oss) {
385 } else if (using_netjack) {
391 EngineControl::engine_running ()
393 jack_status_t status;
394 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
397 jack_client_close (c);
404 EngineControl::start_engine ()
407 std::string cwd = "/tmp";
410 build_command_line (args);
412 Glib::ustring jackdrc_path = Glib::get_home_dir();
413 jackdrc_path += "/.jackdrc";
415 ofstream jackdrc (jackdrc_path.c_str());
417 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
421 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
422 jackdrc << (*i) << ' ';
430 spawn_async_with_pipes (cwd, args, SpawnFlags (0), sigc::slot<void>(), &engine_pid, &engine_stdin, &engine_stdout, &engine_stderr);
433 catch (Glib::Exception& err) {
434 error << _("could not start JACK server: ") << err.what() << endmsg;
443 EngineControl::stop_engine ()
445 close (engine_stdin);
446 close (engine_stderr);
447 close (engine_stdout);
448 spawn_close_pid (engine_pid);
453 EngineControl::realtime_changed ()
455 priority_spinner.set_sensitive (realtime_button.get_active());
459 EngineControl::enumerate_devices ()
461 /* note: case matters for the map keys */
464 devices["CoreAudio"] = enumerate_coreaudio_devices ();
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 ();
476 EngineControl::enumerate_coreaudio_devices ()
483 EngineControl::enumerate_alsa_devices ()
486 devs.push_back ("hw:0");
487 devs.push_back ("hw:1");
488 devs.push_back ("plughw:0");
489 devs.push_back ("plughw:1");
493 EngineControl::enumerate_ffado_devices ()
499 EngineControl::enumerate_oss_devices ()
505 EngineControl::enumerate_dummy_devices ()
511 EngineControl::enumerate_netjack_devices ()
519 EngineControl::driver_changed ()
521 string driver = driver_combo.get_active_text();
522 vector<string>& strings = devices[driver];
524 set_popdown_strings (interface_combo, strings);
526 if (!strings.empty()) {
527 interface_combo.set_active_text (strings.front());
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);
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);
546 EngineControl::get_rate ()
548 return atoi (sample_rate_combo.get_active_text ());
552 EngineControl::redisplay_latency ()
554 uint32_t rate = get_rate();
555 float periods = periods_adjustment.get_value();
556 float period_size = atof (period_size_combo.get_active_text());
559 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
561 latency_label.set_text (buf);
565 EngineControl::audio_mode_changed ()
567 Glib::ustring str = audio_mode_combo.get_active_text();
569 if (str == _("Duplex")) {
570 input_device_combo.set_sensitive (false);
571 output_device_combo.set_sensitive (false);
573 input_device_combo.set_sensitive (true);
574 output_device_combo.set_sensitive (true);
579 EngineControl::find_jack_servers (vector<string>& strings)
582 if (Profile->get_single_package()) {
584 /* this magic lets us finds the path to the OSX bundle, and then
585 we infer JACK's location from there
588 CFURLRef pluginRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
589 CFStringRef macPath = CFURLCopyFileSystemPath(pluginRef,
590 kCFURLPOSIXPathStyle);
591 std::string path = CFStringGetCStringPtr(macPath,
592 CFStringGetSystemEncoding());
593 CFRelease(pluginRef);
598 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
599 strings.push_back ();
601 warning << _("JACK appears to be missing from the Ardour bundle") << endmsg;
605 if (Glib::file_test ("/usr/bin/jackd", FILE_TEST_EXISTS)) {
606 strings.push_back ("/usr/bin/jackd");
608 if (Glib::file_test ("/usr/local/bin/jackd", FILE_TEST_EXISTS)) {
609 strings.push_back ("/usr/local/bin/jackd");
611 if (Glib::file_test ("/opt/bin/jackd", FILE_TEST_EXISTS)) {
612 strings.push_back ("/opt/bin/jackd");
614 if (Glib::file_test ("/usr/bin/jackdmp", FILE_TEST_EXISTS)) {
615 strings.push_back ("/usr/bin/jackd");
617 if (Glib::file_test ("/usr/local/bin/jackdmp", FILE_TEST_EXISTS)) {
618 strings.push_back ("/usr/local/bin/jackd");
620 if (Glib::file_test ("/opt/bin/jackdmp", FILE_TEST_EXISTS)) {
621 strings.push_back ("/opt/bin/jackd");