try to name/number new routes-from-templates to avoid colliding names if adding more...
[ardour2.git] / gtk2_ardour / engine_dialog.cc
blob22dd1e39fd00604389d77159ef0711c4992f3366
1 #include <vector>
2 #include <cmath>
3 #include <fstream>
4 #include <map>
6 #include <glibmm.h>
7 #include <gtkmm/messagedialog.h>
8 #include <pbd/xml++.h>
10 #ifdef __APPLE__
11 #include <CoreAudio/CoreAudio.h>
12 #include <CoreFoundation/CFString.h>
13 #include <sys/param.h>
14 #include <mach-o/dyld.h>
15 #else
16 #include <alsa/asoundlib.h>
17 #endif
19 #include <ardour/profile.h>
20 #include <jack/jack.h>
22 #include <gtkmm/stock.h>
23 #include <gtkmm2ext/utils.h>
25 #include <pbd/convert.h>
26 #include <pbd/error.h>
27 #include <pbd/pathscanner.h>
29 #ifdef __APPLE
30 #include <CFBundle.h>
31 #endif
33 #include "engine_dialog.h"
34 #include "i18n.h"
36 using namespace std;
37 using namespace Gtk;
38 using namespace Gtkmm2ext;
39 using namespace PBD;
40 using namespace Glib;
42 EngineControl::EngineControl ()
43 : periods_adjustment (2, 2, 16, 1, 2),
44 periods_spinner (periods_adjustment),
45 priority_adjustment (60, 10, 90, 1, 10),
46 priority_spinner (priority_adjustment),
47 ports_adjustment (128, 8, 1024, 1, 16),
48 ports_spinner (ports_adjustment),
49 realtime_button (_("Realtime")),
50 no_memory_lock_button (_("Do not lock memory")),
51 unlock_memory_button (_("Unlock memory")),
52 soft_mode_button (_("No zombies")),
53 monitor_button (_("Provide monitor ports")),
54 force16bit_button (_("Force 16 bit")),
55 hw_monitor_button (_("H/W monitoring")),
56 hw_meter_button (_("H/W metering")),
57 verbose_output_button (_("Verbose output")),
58 start_button (_("Start")),
59 stop_button (_("Stop")),
60 #ifdef __APPLE__
61 basic_packer (5, 2),
62 options_packer (4, 2),
63 device_packer (4, 2)
64 #else
65 basic_packer (8, 2),
66 options_packer (14, 2),
67 device_packer (6, 2)
68 #endif
70 using namespace Notebook_Helpers;
71 Label* label;
72 vector<string> strings;
73 int row = 0;
75 _used = false;
77 strings.push_back (_("8000Hz"));
78 strings.push_back (_("22050Hz"));
79 strings.push_back (_("44100Hz"));
80 strings.push_back (_("48000Hz"));
81 strings.push_back (_("88200Hz"));
82 strings.push_back (_("96000Hz"));
83 strings.push_back (_("192000Hz"));
84 set_popdown_strings (sample_rate_combo, strings);
85 sample_rate_combo.set_active_text ("48000Hz");
87 strings.clear ();
88 strings.push_back ("32");
89 strings.push_back ("64");
90 strings.push_back ("128");
91 strings.push_back ("256");
92 strings.push_back ("512");
93 strings.push_back ("1024");
94 strings.push_back ("2048");
95 strings.push_back ("4096");
96 strings.push_back ("8192");
97 set_popdown_strings (period_size_combo, strings);
98 period_size_combo.set_active_text ("1024");
100 strings.clear ();
101 strings.push_back (_("None"));
102 strings.push_back (_("Triangular"));
103 strings.push_back (_("Rectangular"));
104 strings.push_back (_("Shaped"));
105 set_popdown_strings (dither_mode_combo, strings);
106 dither_mode_combo.set_active_text (_("None"));
108 /* basic parameters */
110 basic_packer.set_spacings (6);
112 strings.clear ();
113 #ifdef __APPLE__
114 strings.push_back (X_("CoreAudio"));
115 #else
116 strings.push_back (X_("ALSA"));
117 strings.push_back (X_("OSS"));
118 strings.push_back (X_("FFADO"));
119 #endif
120 strings.push_back (X_("NetJACK"));
121 strings.push_back (X_("Dummy"));
122 set_popdown_strings (driver_combo, strings);
123 driver_combo.set_active_text (strings.front());
125 driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
126 driver_changed ();
128 strings.clear ();
129 strings.push_back (_("Playback/Recording on 1 Device"));
130 strings.push_back (_("Playback/Recording on 2 Devices"));
131 strings.push_back (_("Playback only"));
132 strings.push_back (_("Recording only"));
133 set_popdown_strings (audio_mode_combo, strings);
134 audio_mode_combo.set_active_text (strings.front());
136 audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
137 audio_mode_changed ();
139 row = 0;
141 label = manage (new Label (_("Driver")));
142 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
143 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
144 row++;
146 label = manage (new Label (_("Interface")));
147 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
148 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
149 row++;
151 label = manage (new Label (_("Sample Rate")));
152 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
153 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
154 row++;
156 label = manage (new Label (_("Buffer size")));
157 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
158 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
159 row++;
161 #ifndef __APPLE__
162 label = manage (new Label (_("Number of buffers")));
163 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
164 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
165 periods_spinner.set_value (2);
166 row++;
167 #endif
169 label = manage (new Label (_("Approximate latency")));
170 label->set_alignment (0.0, 0.5);
171 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
172 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
173 row++;
175 sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
176 periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
177 period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
178 redisplay_latency();
179 row++;
180 /* no audio mode with CoreAudio, its duplex or nuthin' */
182 #ifndef __APPLE__
183 label = manage (new Label (_("Audio Mode")));
184 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
185 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
186 row++;
187 #endif
189 interface_combo.set_size_request (125, -1);
190 input_device_combo.set_size_request (125, -1);
191 output_device_combo.set_size_request (125, -1);
195 if (engine_running()) {
196 start_button.set_sensitive (false);
197 } else {
198 stop_button.set_sensitive (false);
201 start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
202 stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
205 button_box.pack_start (start_button, false, false);
206 button_box.pack_start (stop_button, false, false);
208 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
210 /* options */
212 options_packer.set_spacings (6);
213 row = 0;
215 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
216 ++row;
218 realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
219 realtime_changed ();
221 #ifndef __APPLE__
222 label = manage (new Label (_("Realtime Priority")));
223 label->set_alignment (1.0, 0.5);
224 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
225 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
226 ++row;
227 priority_spinner.set_value (60);
229 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
230 ++row;
231 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
232 ++row;
233 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
234 ++row;
235 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
236 ++row;
237 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
238 ++row;
239 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
240 ++row;
241 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
242 ++row;
243 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
244 ++row;
245 #else
246 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
247 ++row;
248 #endif
250 strings.clear ();
251 strings.push_back (_("Ignore"));
252 strings.push_back ("500 msec");
253 strings.push_back ("1 sec");
254 strings.push_back ("2 sec");
255 strings.push_back ("10 sec");
256 set_popdown_strings (timeout_combo, strings);
257 timeout_combo.set_active_text (strings.front ());
259 label = manage (new Label (_("Client timeout")));
260 label->set_alignment (1.0, 0.5);
261 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
262 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
263 ++row;
265 label = manage (new Label (_("Number of ports")));
266 label->set_alignment (1.0, 0.5);
267 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
268 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
269 ++row;
271 #ifndef __APPLE__
272 label = manage (new Label (_("Dither")));
273 label->set_alignment (1.0, 0.5);
274 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
275 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
276 ++row;
277 #endif
279 /* defer server stuff till later */
280 server_row = row++;
282 /* device settings */
284 device_packer.set_spacings (6);
285 row = 0;
287 #ifndef __APPLE__
288 label = manage (new Label (_("Input device")));
289 label->set_alignment (1.0, 0.5);
290 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
291 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
292 ++row;
293 label = manage (new Label (_("Output device")));
294 label->set_alignment (1.0, 0.5);
295 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
296 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
297 ++row;
298 #endif
299 label = manage (new Label (_("Input channels")));
300 label->set_alignment (1.0, 0.5);
301 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
302 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
303 ++row;
304 label = manage (new Label (_("Output channels")));
305 label->set_alignment (1.0, 0.5);
306 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
307 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
308 ++row;
309 label = manage (new Label (_("Hardware input latency (samples)")));
310 label->set_alignment (1.0, 0.5);
311 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
312 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
313 ++row;
314 label = manage (new Label (_("Hardware output latency (samples)")));
315 label->set_alignment (1.0, 0.5);
316 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
317 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
318 ++row;
320 basic_hbox.pack_start (basic_packer, false, false);
321 options_hbox.pack_start (options_packer, false, false);
323 device_packer.set_border_width (12);
324 options_packer.set_border_width (12);
325 basic_packer.set_border_width (12);
327 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
328 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
329 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
330 notebook.set_border_width (12);
332 set_border_width (12);
333 pack_start (notebook);
336 EngineControl::~EngineControl ()
341 void
342 EngineControl::discover_servers ()
344 find_jack_servers (server_strings);
346 if (server_strings.empty()) {
347 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
348 /*NOTREACHED*/
351 set_popdown_strings (serverpath_combo, server_strings);
352 serverpath_combo.set_active_text (server_strings.front());
354 if (server_strings.size() > 1) {
355 Gtk::Label* label = manage (new Label (_("Server:")));
356 options_packer.attach (*label, 0, 1, server_row, server_row + 1, FILL|EXPAND, (AttachOptions) 0);
357 label->set_alignment (0.0, 0.5);
358 options_packer.attach (serverpath_combo, 1, 2, server_row, server_row + 1, FILL|EXPAND, (AttachOptions) 0);
362 void
363 EngineControl::build_command_line (vector<string>& cmd)
365 string str;
366 string driver;
367 bool using_oss = false;
368 bool using_alsa = false;
369 bool using_coreaudio = false;
370 bool using_netjack = false;
371 bool using_ffado = false;
372 bool using_dummy = false;
374 /* first, path to jackd */
376 cmd.push_back (serverpath_combo.get_active_text ());
378 /* now jackd arguments */
380 str = timeout_combo.get_active_text ();
381 if (str != _("Ignore")) {
382 double secs;
383 uint32_t msecs;
384 secs = atof (str);
385 msecs = (uint32_t) floor (secs * 1000.0);
386 cmd.push_back ("-t");
387 cmd.push_back (to_string (msecs, std::dec));
390 if (no_memory_lock_button.get_active()) {
391 cmd.push_back ("-m"); /* no munlock */
394 cmd.push_back ("-p"); /* port max */
395 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
397 if (realtime_button.get_active()) {
398 cmd.push_back ("-R");
399 cmd.push_back ("-P");
400 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
403 if (unlock_memory_button.get_active()) {
404 cmd.push_back ("-u");
407 if (verbose_output_button.get_active()) {
408 cmd.push_back ("-v");
411 /* now add fixed arguments (not user-selectable) */
413 cmd.push_back ("-T"); // temporary */
415 /* next the driver */
417 cmd.push_back ("-d");
419 driver = driver_combo.get_active_text ();
420 if (driver == X_("ALSA")) {
421 using_alsa = true;
422 cmd.push_back ("alsa");
423 } else if (driver == X_("OSS")) {
424 using_oss = true;
425 cmd.push_back ("oss");
426 } else if (driver == X_("CoreAudio")) {
427 using_coreaudio = true;
428 cmd.push_back ("coreaudio");
429 } else if (driver == X_("NetJACK")) {
430 using_netjack = true;
431 cmd.push_back ("netjack");
432 } else if (driver == X_("FFADO")) {
433 using_ffado = true;
435 /* do this until FFADO becomes the standard */
437 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
439 if (hack) {
440 cmd.push_back (hack);
441 } else {
442 cmd.push_back ("freebob");
445 } else if ( driver == X_("Dummy")) {
446 using_dummy = true;
447 cmd.push_back ("dummy");
450 /* driver arguments */
452 if (!using_coreaudio) {
453 str = audio_mode_combo.get_active_text();
455 if (str == _("Playback/Recording on 1 Device")) {
457 /* relax */
459 } else if (str == _("Playback/Recording on 2 Devices")) {
461 string input_device = get_device_name (driver, input_device_combo.get_active_text());
462 string output_device = get_device_name (driver, output_device_combo.get_active_text());
464 if (input_device.empty() || output_device.empty()) {
465 cmd.clear ();
466 return;
469 cmd.push_back ("-C");
470 cmd.push_back (input_device);
471 cmd.push_back ("-P");
472 cmd.push_back (output_device);
474 } else if (str == _("Playback only")) {
475 cmd.push_back ("-P");
476 } else if (str == _("Recording only")) {
477 cmd.push_back ("-C");
480 if (! using_dummy ) {
481 cmd.push_back ("-n");
482 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
486 cmd.push_back ("-r");
487 cmd.push_back (to_string (get_rate(), std::dec));
489 cmd.push_back ("-p");
490 cmd.push_back (period_size_combo.get_active_text());
492 if (using_alsa) {
494 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
496 string device = get_device_name (driver, interface_combo.get_active_text());
497 if (device.empty()) {
498 cmd.clear ();
499 return;
502 cmd.push_back ("-d");
503 cmd.push_back (device);
506 if (hw_meter_button.get_active()) {
507 cmd.push_back ("-M");
510 if (hw_monitor_button.get_active()) {
511 cmd.push_back ("-H");
514 str = dither_mode_combo.get_active_text();
516 if (str == _("None")) {
517 } else if (str == _("Triangular")) {
518 cmd.push_back ("-z triangular");
519 } else if (str == _("Rectangular")) {
520 cmd.push_back ("-z rectangular");
521 } else if (str == _("Shaped")) {
522 cmd.push_back ("-z shaped");
525 if (force16bit_button.get_active()) {
526 cmd.push_back ("-S");
529 if (soft_mode_button.get_active()) {
530 cmd.push_back ("-s");
533 } else if (using_coreaudio) {
535 #ifdef __APPLE__
536 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
538 string device = get_device_name (driver, interface_combo.get_active_text());
539 if (device.empty()) {
540 cmd.clear ();
541 return;
544 cmd.push_back ("-d");
545 cmd.push_back (device);
546 #endif
548 } else if (using_oss) {
550 } else if (using_netjack) {
555 bool
556 EngineControl::engine_running ()
558 jack_status_t status;
559 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
561 if (status == 0) {
562 jack_client_close (c);
563 return true;
565 return false;
569 EngineControl::setup_engine ()
571 vector<string> args;
572 std::string cwd = "/tmp";
574 build_command_line (args);
576 if (args.empty()) {
577 return 1; // try again
580 Glib::ustring jackdrc_path = Glib::get_home_dir();
581 jackdrc_path += "/.jackdrc";
583 ofstream jackdrc (jackdrc_path.c_str());
584 if (!jackdrc) {
585 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
586 return -1;
588 cerr << "JACK COMMAND: ";
589 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
590 cerr << (*i) << ' ';
591 jackdrc << (*i) << ' ';
593 cerr << endl;
594 jackdrc << endl;
595 jackdrc.close ();
597 _used = true;
599 return 0;
602 void
603 EngineControl::realtime_changed ()
605 #ifndef __APPLE__
606 priority_spinner.set_sensitive (realtime_button.get_active());
607 #endif
610 void
611 EngineControl::enumerate_devices (const string& driver)
613 /* note: case matters for the map keys */
615 if (driver == "CoreAudio") {
616 #ifdef __APPLE__
617 devices[driver] = enumerate_coreaudio_devices ();
618 #endif
620 #ifndef __APPLE__
621 } else if (driver == "ALSA") {
622 devices[driver] = enumerate_alsa_devices ();
623 } else if (driver == "FFADO") {
624 devices[driver] = enumerate_ffado_devices ();
625 } else if (driver == "OSS") {
626 devices[driver] = enumerate_oss_devices ();
627 } else if (driver == "Dummy") {
628 devices[driver] = enumerate_dummy_devices ();
629 } else if (driver == "NetJACK") {
630 devices[driver] = enumerate_netjack_devices ();
632 #else
634 #endif
637 #ifdef __APPLE__
638 static OSStatus
639 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
641 UInt32 size = sizeof(CFStringRef);
642 CFStringRef UI;
643 OSStatus res = AudioDeviceGetProperty(id, 0, false,
644 kAudioDevicePropertyDeviceUID, &size, &UI);
645 if (res == noErr)
646 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
647 CFRelease(UI);
648 return res;
651 vector<string>
652 EngineControl::enumerate_coreaudio_devices ()
654 vector<string> devs;
656 // Find out how many Core Audio devices are there, if any...
657 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
658 OSStatus err;
659 Boolean isWritable;
660 size_t outSize = sizeof(isWritable);
662 backend_devs.clear ();
664 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
665 &outSize, &isWritable);
666 if (err == noErr) {
667 // Calculate the number of device available...
668 int numCoreDevices = outSize / sizeof(AudioDeviceID);
669 // Make space for the devices we are about to get...
670 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
671 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
672 &outSize, (void *) coreDeviceIDs);
673 if (err == noErr) {
674 // Look for the CoreAudio device name...
675 char coreDeviceName[256];
676 size_t nameSize;
678 for (int i = 0; i < numCoreDevices; i++) {
680 nameSize = sizeof (coreDeviceName);
682 /* enforce duplex devices only */
684 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
685 0, true, kAudioDevicePropertyStreams,
686 &outSize, &isWritable);
688 if (err != noErr || outSize == 0) {
689 continue;
692 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
693 0, false, kAudioDevicePropertyStreams,
694 &outSize, &isWritable);
696 if (err != noErr || outSize == 0) {
697 continue;
700 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
701 0, true, kAudioDevicePropertyDeviceName,
702 &outSize, &isWritable);
703 if (err == noErr) {
704 err = AudioDeviceGetProperty(coreDeviceIDs[i],
705 0, true, kAudioDevicePropertyDeviceName,
706 &nameSize, (void *) coreDeviceName);
707 if (err == noErr) {
708 char drivername[128];
710 // this returns the unique id for the device
711 // that must be used on the commandline for jack
713 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
714 devs.push_back (coreDeviceName);
715 backend_devs.push_back (drivername);
721 delete [] coreDeviceIDs;
725 if (devs.size() == 0) {
726 MessageDialog msg (_("\
727 You do not have any audio devices capable of\n\
728 simultaneous playback and recording.\n\n\
729 Please use Applications -> Utilities -> Audio MIDI Setup\n\
730 to create an \"aggregrate\" device, or install a suitable\n\
731 audio interface.\n\n\
732 Please send email to Apple and ask them why new Macs\n\
733 have no duplex audio device.\n\n\
734 Alternatively, if you really want just playback\n\
735 or recording but not both, start JACK before running\n\
736 Ardour and choose the relevant device then."
738 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
739 msg.set_title (_("No suitable audio devices"));
740 msg.set_position (Gtk::WIN_POS_MOUSE);
741 msg.run ();
742 exit (1);
746 return devs;
748 #else
749 vector<string>
750 EngineControl::enumerate_alsa_devices ()
752 vector<string> devs;
754 snd_ctl_t *handle;
755 snd_ctl_card_info_t *info;
756 snd_pcm_info_t *pcminfo;
757 snd_ctl_card_info_alloca(&info);
758 snd_pcm_info_alloca(&pcminfo);
759 string devname;
760 int cardnum = -1;
761 int device = -1;
763 backend_devs.clear ();
765 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
767 devname = "hw:";
768 devname += to_string (cardnum, std::dec);
770 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
772 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
774 bool have_playback = false;
775 bool have_capture = false;
777 /* find duplex devices only */
779 snd_pcm_info_set_device (pcminfo, device);
780 snd_pcm_info_set_subdevice (pcminfo, 0);
781 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
783 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
784 have_capture = true;
787 snd_pcm_info_set_device (pcminfo, device);
788 snd_pcm_info_set_subdevice (pcminfo, 0);
789 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
791 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
792 have_playback = true;
795 if (have_capture && have_playback) {
796 devs.push_back (snd_pcm_info_get_name (pcminfo));
797 devname += ',';
798 devname += to_string (device, std::dec);
799 backend_devs.push_back (devname);
803 snd_ctl_close(handle);
807 return devs;
810 vector<string>
811 EngineControl::enumerate_ffado_devices ()
813 vector<string> devs;
814 backend_devs.clear ();
815 return devs;
818 vector<string>
819 EngineControl::enumerate_oss_devices ()
821 vector<string> devs;
822 return devs;
824 vector<string>
825 EngineControl::enumerate_dummy_devices ()
827 vector<string> devs;
828 return devs;
830 vector<string>
831 EngineControl::enumerate_netjack_devices ()
833 vector<string> devs;
834 return devs;
836 #endif
838 void
839 EngineControl::driver_changed ()
841 string driver = driver_combo.get_active_text();
842 string::size_type maxlen = 0;
843 int maxindex = -1;
844 int n = 0;
846 enumerate_devices (driver);
848 vector<string>& strings = devices[driver];
850 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
851 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
852 return;
855 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
856 if ((*i).length() > maxlen) {
857 maxlen = (*i).length();
858 maxindex = n;
862 set_popdown_strings (interface_combo, strings);
863 set_popdown_strings (input_device_combo, strings);
864 set_popdown_strings (output_device_combo, strings);
866 if (!strings.empty()) {
867 interface_combo.set_active_text (strings.front());
868 input_device_combo.set_active_text (strings.front());
869 output_device_combo.set_active_text (strings.front());
872 if (driver == "ALSA") {
873 soft_mode_button.set_sensitive (true);
874 force16bit_button.set_sensitive (true);
875 hw_monitor_button.set_sensitive (true);
876 hw_meter_button.set_sensitive (true);
877 monitor_button.set_sensitive (true);
878 } else {
879 soft_mode_button.set_sensitive (false);
880 force16bit_button.set_sensitive (false);
881 hw_monitor_button.set_sensitive (false);
882 hw_meter_button.set_sensitive (false);
883 monitor_button.set_sensitive (false);
887 uint32_t
888 EngineControl::get_rate ()
890 return atoi (sample_rate_combo.get_active_text ());
893 void
894 EngineControl::redisplay_latency ()
896 uint32_t rate = get_rate();
897 #ifdef __APPLE_
898 float periods = 2;
899 #else
900 float periods = periods_adjustment.get_value();
901 #endif
902 float period_size = atof (period_size_combo.get_active_text());
904 char buf[32];
905 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
907 latency_label.set_text (buf);
910 void
911 EngineControl::audio_mode_changed ()
913 Glib::ustring str = audio_mode_combo.get_active_text();
915 if (str == _("Playback/Recording on 1 Device")) {
916 input_device_combo.set_sensitive (false);
917 output_device_combo.set_sensitive (false);
918 } else if (str == _("Playback/Recording on 2 Devices")) {
919 input_device_combo.set_sensitive (true);
920 output_device_combo.set_sensitive (true);
921 } else if (str == _("Playback only")) {
922 output_device_combo.set_sensitive (true);
923 } else if (str == _("Recording only")) {
924 input_device_combo.set_sensitive (true);
928 static bool jack_server_filter(const string& str, void *arg)
930 return str == "jackd" || str == "jackdmp";
933 void
934 EngineControl::find_jack_servers (vector<string>& strings)
936 #ifdef __APPLE__
937 /* this magic lets us finds the path to the OSX bundle, and then
938 we infer JACK's location from there
941 char execpath[MAXPATHLEN+1];
942 uint32_t pathsz = sizeof (execpath);
944 _NSGetExecutablePath (execpath, &pathsz);
946 string path (Glib::path_get_dirname (execpath));
947 path += "/jackd";
949 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
950 strings.push_back (path);
953 if (getenv ("ARDOUR_WITH_JACK")) {
954 /* no other options - only use the JACK we supply */
955 if (strings.empty()) {
956 fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
957 /*NOTREACHED*/
959 return;
961 #else
962 string path;
963 #endif
965 PathScanner scanner;
966 vector<string *> *jack_servers;
967 std::map<string,int> un;
968 char *p;
969 bool need_minimal_path = false;
971 p = getenv ("PATH");
973 if (p && *p) {
974 path = p;
975 } else {
976 need_minimal_path = true;
979 #ifdef __APPLE__
980 // many mac users don't have PATH set up to include
981 // likely installed locations of JACK
982 need_minimal_path = true;
983 #endif
985 if (need_minimal_path) {
986 if (path.empty()) {
987 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
988 } else {
989 path += ":/usr/local/bin:/opt/local/bin";
993 #ifdef __APPLE__
994 // push it back into the environment so that auto-started JACK can find it.
995 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
996 setenv ("PATH", path.c_str(), 1);
997 #endif
999 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1001 vector<string *>::iterator iter;
1003 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1004 string p = **iter;
1006 if (un[p]++ == 0) {
1007 strings.push_back(p);
1012 string
1013 EngineControl::get_device_name (const string& driver, const string& human_readable)
1015 vector<string>::iterator n;
1016 vector<string>::iterator i;
1018 if (human_readable.empty()) {
1019 /* this can happen if the user's .ardourrc file has a device name from
1020 another computer system in it
1022 MessageDialog msg (_("You need to choose an audio device first."));
1023 msg.run ();
1024 return string();
1027 if (backend_devs.empty()) {
1028 return human_readable;
1031 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1032 if (human_readable == (*i)) {
1033 return (*n);
1037 if (i == devices[driver].end()) {
1038 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1041 return string();
1044 XMLNode&
1045 EngineControl::get_state ()
1047 XMLNode* root = new XMLNode ("AudioSetup");
1048 XMLNode* child;
1049 Glib::ustring path;
1051 child = new XMLNode ("periods");
1052 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1053 root->add_child_nocopy (*child);
1055 child = new XMLNode ("priority");
1056 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1057 root->add_child_nocopy (*child);
1059 child = new XMLNode ("ports");
1060 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1061 root->add_child_nocopy (*child);
1063 child = new XMLNode ("inchannels");
1064 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1065 root->add_child_nocopy (*child);
1067 child = new XMLNode ("outchannels");
1068 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1069 root->add_child_nocopy (*child);
1071 child = new XMLNode ("inlatency");
1072 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1073 root->add_child_nocopy (*child);
1075 child = new XMLNode ("outlatency");
1076 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1077 root->add_child_nocopy (*child);
1079 child = new XMLNode ("realtime");
1080 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1081 root->add_child_nocopy (*child);
1083 child = new XMLNode ("nomemorylock");
1084 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1085 root->add_child_nocopy (*child);
1087 child = new XMLNode ("unlockmemory");
1088 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1089 root->add_child_nocopy (*child);
1091 child = new XMLNode ("softmode");
1092 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1093 root->add_child_nocopy (*child);
1095 child = new XMLNode ("force16bit");
1096 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1097 root->add_child_nocopy (*child);
1099 child = new XMLNode ("hwmonitor");
1100 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1101 root->add_child_nocopy (*child);
1103 child = new XMLNode ("hwmeter");
1104 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1105 root->add_child_nocopy (*child);
1107 child = new XMLNode ("verbose");
1108 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1109 root->add_child_nocopy (*child);
1111 child = new XMLNode ("samplerate");
1112 child->add_property ("val", sample_rate_combo.get_active_text());
1113 root->add_child_nocopy (*child);
1115 child = new XMLNode ("periodsize");
1116 child->add_property ("val", period_size_combo.get_active_text());
1117 root->add_child_nocopy (*child);
1119 child = new XMLNode ("serverpath");
1120 child->add_property ("val", serverpath_combo.get_active_text());
1121 root->add_child_nocopy (*child);
1123 child = new XMLNode ("driver");
1124 child->add_property ("val", driver_combo.get_active_text());
1125 root->add_child_nocopy (*child);
1127 child = new XMLNode ("interface");
1128 child->add_property ("val", interface_combo.get_active_text());
1129 root->add_child_nocopy (*child);
1131 child = new XMLNode ("timeout");
1132 child->add_property ("val", timeout_combo.get_active_text());
1133 root->add_child_nocopy (*child);
1135 child = new XMLNode ("dither");
1136 child->add_property ("val", dither_mode_combo.get_active_text());
1137 root->add_child_nocopy (*child);
1139 child = new XMLNode ("audiomode");
1140 child->add_property ("val", audio_mode_combo.get_active_text());
1141 root->add_child_nocopy (*child);
1143 child = new XMLNode ("inputdevice");
1144 child->add_property ("val", input_device_combo.get_active_text());
1145 root->add_child_nocopy (*child);
1147 child = new XMLNode ("outputdevice");
1148 child->add_property ("val", output_device_combo.get_active_text());
1149 root->add_child_nocopy (*child);
1151 return *root;
1154 void
1155 EngineControl::set_state (const XMLNode& root)
1157 XMLNodeList clist;
1158 XMLNodeConstIterator citer;
1159 XMLNode* child;
1160 XMLProperty* prop = NULL;
1161 bool using_dummy = false;
1163 int val;
1164 string strval;
1166 if ( (child = root.child ("driver"))){
1167 prop = child->property("val");
1168 if (prop && (prop->value() == "Dummy") ) {
1169 using_dummy = true;
1173 clist = root.children();
1175 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1176 if ( prop && (prop->value() == "FFADO" ))
1177 continue;
1178 child = *citer;
1180 prop = child->property ("val");
1182 if (!prop || prop->value().empty()) {
1184 if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1185 continue;
1186 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1187 continue;
1190 strval = prop->value();
1192 /* adjustments/spinners */
1194 if (child->name() == "periods") {
1195 val = atoi (strval);
1196 periods_adjustment.set_value(val);
1197 } else if (child->name() == "priority") {
1198 val = atoi (strval);
1199 priority_adjustment.set_value(val);
1200 } else if (child->name() == "ports") {
1201 val = atoi (strval);
1202 ports_adjustment.set_value(val);
1203 } else if (child->name() == "inchannels") {
1204 val = atoi (strval);
1205 input_channels.set_value(val);
1206 } else if (child->name() == "outchannels") {
1207 val = atoi (strval);
1208 output_channels.set_value(val);
1209 } else if (child->name() == "inlatency") {
1210 val = atoi (strval);
1211 input_latency.set_value(val);
1212 } else if (child->name() == "outlatency") {
1213 val = atoi (strval);
1214 output_latency.set_value(val);
1217 /* buttons */
1219 else if (child->name() == "realtime") {
1220 val = atoi (strval);
1221 realtime_button.set_active(val);
1222 } else if (child->name() == "nomemorylock") {
1223 val = atoi (strval);
1224 no_memory_lock_button.set_active(val);
1225 } else if (child->name() == "unlockmemory") {
1226 val = atoi (strval);
1227 unlock_memory_button.set_active(val);
1228 } else if (child->name() == "softmode") {
1229 val = atoi (strval);
1230 soft_mode_button.set_active(val);
1231 } else if (child->name() == "force16bit") {
1232 val = atoi (strval);
1233 force16bit_button.set_active(val);
1234 } else if (child->name() == "hwmonitor") {
1235 val = atoi (strval);
1236 hw_monitor_button.set_active(val);
1237 } else if (child->name() == "hwmeter") {
1238 val = atoi (strval);
1239 hw_meter_button.set_active(val);
1240 } else if (child->name() == "verbose") {
1241 val = atoi (strval);
1242 verbose_output_button.set_active(val);
1245 /* combos */
1247 else if (child->name() == "samplerate") {
1248 sample_rate_combo.set_active_text(strval);
1249 } else if (child->name() == "periodsize") {
1250 period_size_combo.set_active_text(strval);
1251 } else if (child->name() == "serverpath") {
1253 /* only attempt to set this if we have bothered to look
1254 up server names already. otherwise this is all
1255 redundant (actually, all of this dialog/widget
1256 is redundant in that case ...)
1260 if (!server_strings.empty()) {
1261 /* do not allow us to use a server path that doesn't
1262 exist on this system. this handles cases where
1263 the user has an RC file listing a serverpath
1264 from some other machine.
1266 vector<string>::iterator x;
1267 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1268 if (*x == strval) {
1269 break;
1272 if (x != server_strings.end()) {
1273 serverpath_combo.set_active_text (strval);
1274 } else {
1275 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1276 strval)
1277 << endmsg;
1281 } else if (child->name() == "driver") {
1282 driver_combo.set_active_text(strval);
1283 } else if (child->name() == "interface") {
1284 interface_combo.set_active_text(strval);
1285 } else if (child->name() == "timeout") {
1286 timeout_combo.set_active_text(strval);
1287 } else if (child->name() == "dither") {
1288 dither_mode_combo.set_active_text(strval);
1289 } else if (child->name() == "audiomode") {
1290 audio_mode_combo.set_active_text(strval);
1291 } else if (child->name() == "inputdevice") {
1292 input_device_combo.set_active_text(strval);
1293 } else if (child->name() == "outputdevice") {
1294 output_device_combo.set_active_text(strval);