clean up & extend handling of various AU Component types & subtypes - makes AU NetSen...
[ardour2.git] / gtk2_ardour / engine_dialog.cc
blobcf08f4ad680891d090704a77c435782e616846e0
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 find_jack_servers (server_strings);
281 if (server_strings.empty()) {
282 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
283 /*NOTREACHED*/
286 set_popdown_strings (serverpath_combo, server_strings);
287 serverpath_combo.set_active_text (server_strings.front());
289 if (server_strings.size() > 1) {
290 label = manage (new Label (_("Server:")));
291 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
292 label->set_alignment (0.0, 0.5);
293 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
294 ++row;
297 /* device settings */
299 device_packer.set_spacings (6);
300 row = 0;
302 #ifndef __APPLE__
303 label = manage (new Label (_("Input device")));
304 label->set_alignment (1.0, 0.5);
305 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
306 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
307 ++row;
308 label = manage (new Label (_("Output device")));
309 label->set_alignment (1.0, 0.5);
310 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
311 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
312 ++row;
313 #endif
314 label = manage (new Label (_("Input channels")));
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 (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
318 ++row;
319 label = manage (new Label (_("Output channels")));
320 label->set_alignment (1.0, 0.5);
321 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
322 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
323 ++row;
324 label = manage (new Label (_("Hardware input latency (samples)")));
325 label->set_alignment (1.0, 0.5);
326 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
327 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
328 ++row;
329 label = manage (new Label (_("Hardware output latency (samples)")));
330 label->set_alignment (1.0, 0.5);
331 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
332 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
333 ++row;
335 basic_hbox.pack_start (basic_packer, false, false);
336 options_hbox.pack_start (options_packer, false, false);
338 device_packer.set_border_width (12);
339 options_packer.set_border_width (12);
340 basic_packer.set_border_width (12);
342 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
343 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
344 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
345 notebook.set_border_width (12);
347 set_border_width (12);
348 pack_start (notebook);
351 EngineControl::~EngineControl ()
356 void
357 EngineControl::build_command_line (vector<string>& cmd)
359 string str;
360 string driver;
361 bool using_oss = false;
362 bool using_alsa = false;
363 bool using_coreaudio = false;
364 bool using_netjack = false;
365 bool using_ffado = false;
366 bool using_dummy = false;
368 /* first, path to jackd */
370 cmd.push_back (serverpath_combo.get_active_text ());
372 /* now jackd arguments */
374 str = timeout_combo.get_active_text ();
375 if (str != _("Ignore")) {
376 double secs;
377 uint32_t msecs;
378 secs = atof (str);
379 msecs = (uint32_t) floor (secs * 1000.0);
380 cmd.push_back ("-t");
381 cmd.push_back (to_string (msecs, std::dec));
384 if (no_memory_lock_button.get_active()) {
385 cmd.push_back ("-m"); /* no munlock */
388 cmd.push_back ("-p"); /* port max */
389 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
391 if (realtime_button.get_active()) {
392 cmd.push_back ("-R");
393 cmd.push_back ("-P");
394 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
397 if (unlock_memory_button.get_active()) {
398 cmd.push_back ("-u");
401 if (verbose_output_button.get_active()) {
402 cmd.push_back ("-v");
405 /* now add fixed arguments (not user-selectable) */
407 cmd.push_back ("-T"); // temporary */
409 /* next the driver */
411 cmd.push_back ("-d");
413 driver = driver_combo.get_active_text ();
414 if (driver == X_("ALSA")) {
415 using_alsa = true;
416 cmd.push_back ("alsa");
417 } else if (driver == X_("OSS")) {
418 using_oss = true;
419 cmd.push_back ("oss");
420 } else if (driver == X_("CoreAudio")) {
421 using_coreaudio = true;
422 cmd.push_back ("coreaudio");
423 } else if (driver == X_("NetJACK")) {
424 using_netjack = true;
425 cmd.push_back ("netjack");
426 } else if (driver == X_("FFADO")) {
427 using_ffado = true;
429 /* do this until FFADO becomes the standard */
431 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
433 if (hack) {
434 cmd.push_back (hack);
435 } else {
436 cmd.push_back ("freebob");
439 } else if ( driver == X_("Dummy")) {
440 using_dummy = true;
441 cmd.push_back ("dummy");
444 /* driver arguments */
446 if (!using_coreaudio) {
447 str = audio_mode_combo.get_active_text();
449 if (str == _("Playback/Recording on 1 Device")) {
451 /* relax */
453 } else if (str == _("Playback/Recording on 2 Devices")) {
455 string input_device = get_device_name (driver, input_device_combo.get_active_text());
456 string output_device = get_device_name (driver, output_device_combo.get_active_text());
458 if (input_device.empty() || output_device.empty()) {
459 cmd.clear ();
460 return;
463 cmd.push_back ("-C");
464 cmd.push_back (input_device);
465 cmd.push_back ("-P");
466 cmd.push_back (output_device);
468 } else if (str == _("Playback only")) {
469 cmd.push_back ("-P");
470 } else if (str == _("Recording only")) {
471 cmd.push_back ("-C");
474 if (! using_dummy ) {
475 cmd.push_back ("-n");
476 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
480 cmd.push_back ("-r");
481 cmd.push_back (to_string (get_rate(), std::dec));
483 cmd.push_back ("-p");
484 cmd.push_back (period_size_combo.get_active_text());
486 if (using_alsa) {
488 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
490 string device = get_device_name (driver, interface_combo.get_active_text());
491 if (device.empty()) {
492 cmd.clear ();
493 return;
496 cmd.push_back ("-d");
497 cmd.push_back (device);
500 if (hw_meter_button.get_active()) {
501 cmd.push_back ("-M");
504 if (hw_monitor_button.get_active()) {
505 cmd.push_back ("-H");
508 str = dither_mode_combo.get_active_text();
510 if (str == _("None")) {
511 } else if (str == _("Triangular")) {
512 cmd.push_back ("-z triangular");
513 } else if (str == _("Rectangular")) {
514 cmd.push_back ("-z rectangular");
515 } else if (str == _("Shaped")) {
516 cmd.push_back ("-z shaped");
519 if (force16bit_button.get_active()) {
520 cmd.push_back ("-S");
523 if (soft_mode_button.get_active()) {
524 cmd.push_back ("-s");
527 } else if (using_coreaudio) {
529 #ifdef __APPLE__
530 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
532 string device = get_device_name (driver, interface_combo.get_active_text());
533 if (device.empty()) {
534 cmd.clear ();
535 return;
538 cmd.push_back ("-d");
539 cmd.push_back (device);
540 #endif
542 } else if (using_oss) {
544 } else if (using_netjack) {
549 bool
550 EngineControl::engine_running ()
552 jack_status_t status;
553 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
555 if (status == 0) {
556 jack_client_close (c);
557 return true;
559 return false;
563 EngineControl::setup_engine ()
565 vector<string> args;
566 std::string cwd = "/tmp";
568 build_command_line (args);
570 if (args.empty()) {
571 return 1; // try again
574 Glib::ustring jackdrc_path = Glib::get_home_dir();
575 jackdrc_path += "/.jackdrc";
577 ofstream jackdrc (jackdrc_path.c_str());
578 if (!jackdrc) {
579 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
580 return -1;
582 cerr << "JACK COMMAND: ";
583 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
584 cerr << (*i) << ' ';
585 jackdrc << (*i) << ' ';
587 cerr << endl;
588 jackdrc << endl;
589 jackdrc.close ();
591 _used = true;
593 return 0;
596 void
597 EngineControl::realtime_changed ()
599 #ifndef __APPLE__
600 priority_spinner.set_sensitive (realtime_button.get_active());
601 #endif
604 void
605 EngineControl::enumerate_devices (const string& driver)
607 /* note: case matters for the map keys */
609 if (driver == "CoreAudio") {
610 #ifdef __APPLE__
611 devices[driver] = enumerate_coreaudio_devices ();
612 #endif
614 #ifndef __APPLE__
615 } else if (driver == "ALSA") {
616 devices[driver] = enumerate_alsa_devices ();
617 } else if (driver == "FFADO") {
618 devices[driver] = enumerate_ffado_devices ();
619 } else if (driver == "OSS") {
620 devices[driver] = enumerate_oss_devices ();
621 } else if (driver == "Dummy") {
622 devices[driver] = enumerate_dummy_devices ();
623 } else if (driver == "NetJACK") {
624 devices[driver] = enumerate_netjack_devices ();
626 #else
628 #endif
631 #ifdef __APPLE__
632 static OSStatus
633 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
635 UInt32 size = sizeof(CFStringRef);
636 CFStringRef UI;
637 OSStatus res = AudioDeviceGetProperty(id, 0, false,
638 kAudioDevicePropertyDeviceUID, &size, &UI);
639 if (res == noErr)
640 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
641 CFRelease(UI);
642 return res;
645 vector<string>
646 EngineControl::enumerate_coreaudio_devices ()
648 vector<string> devs;
650 // Find out how many Core Audio devices are there, if any...
651 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
652 OSStatus err;
653 Boolean isWritable;
654 size_t outSize = sizeof(isWritable);
656 backend_devs.clear ();
658 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
659 &outSize, &isWritable);
660 if (err == noErr) {
661 // Calculate the number of device available...
662 int numCoreDevices = outSize / sizeof(AudioDeviceID);
663 // Make space for the devices we are about to get...
664 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
665 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
666 &outSize, (void *) coreDeviceIDs);
667 if (err == noErr) {
668 // Look for the CoreAudio device name...
669 char coreDeviceName[256];
670 size_t nameSize;
672 for (int i = 0; i < numCoreDevices; i++) {
674 nameSize = sizeof (coreDeviceName);
676 /* enforce duplex devices only */
678 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
679 0, true, kAudioDevicePropertyStreams,
680 &outSize, &isWritable);
682 if (err != noErr || outSize == 0) {
683 continue;
686 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
687 0, false, kAudioDevicePropertyStreams,
688 &outSize, &isWritable);
690 if (err != noErr || outSize == 0) {
691 continue;
694 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
695 0, true, kAudioDevicePropertyDeviceName,
696 &outSize, &isWritable);
697 if (err == noErr) {
698 err = AudioDeviceGetProperty(coreDeviceIDs[i],
699 0, true, kAudioDevicePropertyDeviceName,
700 &nameSize, (void *) coreDeviceName);
701 if (err == noErr) {
702 char drivername[128];
704 // this returns the unique id for the device
705 // that must be used on the commandline for jack
707 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
708 devs.push_back (coreDeviceName);
709 backend_devs.push_back (drivername);
715 delete [] coreDeviceIDs;
719 if (devs.size() == 0) {
720 MessageDialog msg (_("\
721 You do not have any audio devices capable of\n\
722 simultaneous playback and recording.\n\n\
723 Please use Applications -> Utilities -> Audio MIDI Setup\n\
724 to create an \"aggregrate\" device, or install a suitable\n\
725 audio interface.\n\n\
726 Please send email to Apple and ask them why new Macs\n\
727 have no duplex audio device.\n\n\
728 Alternatively, if you really want just playback\n\
729 or recording but not both, start JACK before running\n\
730 Ardour and choose the relevant device then."
732 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
733 msg.set_title (_("No suitable audio devices"));
734 msg.set_position (Gtk::WIN_POS_MOUSE);
735 msg.run ();
736 exit (1);
740 return devs;
742 #else
743 vector<string>
744 EngineControl::enumerate_alsa_devices ()
746 vector<string> devs;
748 snd_ctl_t *handle;
749 snd_ctl_card_info_t *info;
750 snd_pcm_info_t *pcminfo;
751 snd_ctl_card_info_alloca(&info);
752 snd_pcm_info_alloca(&pcminfo);
753 string devname;
754 int cardnum = -1;
755 int device = -1;
757 backend_devs.clear ();
759 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
761 devname = "hw:";
762 devname += to_string (cardnum, std::dec);
764 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
766 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
768 bool have_playback = false;
769 bool have_capture = false;
771 /* find duplex devices only */
773 snd_pcm_info_set_device (pcminfo, device);
774 snd_pcm_info_set_subdevice (pcminfo, 0);
775 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
777 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
778 have_capture = true;
781 snd_pcm_info_set_device (pcminfo, device);
782 snd_pcm_info_set_subdevice (pcminfo, 0);
783 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
785 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
786 have_playback = true;
789 if (have_capture && have_playback) {
790 devs.push_back (snd_pcm_info_get_name (pcminfo));
791 devname += ',';
792 devname += to_string (device, std::dec);
793 backend_devs.push_back (devname);
797 snd_ctl_close(handle);
801 return devs;
804 vector<string>
805 EngineControl::enumerate_ffado_devices ()
807 vector<string> devs;
808 backend_devs.clear ();
809 return devs;
812 vector<string>
813 EngineControl::enumerate_oss_devices ()
815 vector<string> devs;
816 return devs;
818 vector<string>
819 EngineControl::enumerate_dummy_devices ()
821 vector<string> devs;
822 return devs;
824 vector<string>
825 EngineControl::enumerate_netjack_devices ()
827 vector<string> devs;
828 return devs;
830 #endif
832 void
833 EngineControl::driver_changed ()
835 string driver = driver_combo.get_active_text();
836 string::size_type maxlen = 0;
837 int maxindex = -1;
838 int n = 0;
840 enumerate_devices (driver);
842 vector<string>& strings = devices[driver];
844 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
845 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
846 return;
849 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
850 if ((*i).length() > maxlen) {
851 maxlen = (*i).length();
852 maxindex = n;
856 set_popdown_strings (interface_combo, strings);
857 set_popdown_strings (input_device_combo, strings);
858 set_popdown_strings (output_device_combo, strings);
860 if (!strings.empty()) {
861 interface_combo.set_active_text (strings.front());
862 input_device_combo.set_active_text (strings.front());
863 output_device_combo.set_active_text (strings.front());
866 if (driver == "ALSA") {
867 soft_mode_button.set_sensitive (true);
868 force16bit_button.set_sensitive (true);
869 hw_monitor_button.set_sensitive (true);
870 hw_meter_button.set_sensitive (true);
871 monitor_button.set_sensitive (true);
872 } else {
873 soft_mode_button.set_sensitive (false);
874 force16bit_button.set_sensitive (false);
875 hw_monitor_button.set_sensitive (false);
876 hw_meter_button.set_sensitive (false);
877 monitor_button.set_sensitive (false);
881 uint32_t
882 EngineControl::get_rate ()
884 return atoi (sample_rate_combo.get_active_text ());
887 void
888 EngineControl::redisplay_latency ()
890 uint32_t rate = get_rate();
891 #ifdef __APPLE_
892 float periods = 2;
893 #else
894 float periods = periods_adjustment.get_value();
895 #endif
896 float period_size = atof (period_size_combo.get_active_text());
898 char buf[32];
899 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
901 latency_label.set_text (buf);
904 void
905 EngineControl::audio_mode_changed ()
907 Glib::ustring str = audio_mode_combo.get_active_text();
909 if (str == _("Playback/Recording on 1 Device")) {
910 input_device_combo.set_sensitive (false);
911 output_device_combo.set_sensitive (false);
912 } else if (str == _("Playback/Recording on 2 Devices")) {
913 input_device_combo.set_sensitive (true);
914 output_device_combo.set_sensitive (true);
915 } else if (str == _("Playback only")) {
916 output_device_combo.set_sensitive (true);
917 } else if (str == _("Recording only")) {
918 input_device_combo.set_sensitive (true);
922 static bool jack_server_filter(const string& str, void *arg)
924 return str == "jackd" || str == "jackdmp";
927 void
928 EngineControl::find_jack_servers (vector<string>& strings)
930 #ifdef __APPLE__
931 /* this magic lets us finds the path to the OSX bundle, and then
932 we infer JACK's location from there
935 char execpath[MAXPATHLEN+1];
936 uint32_t pathsz = sizeof (execpath);
938 _NSGetExecutablePath (execpath, &pathsz);
940 string path (Glib::path_get_dirname (execpath));
941 path += "/jackd";
943 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
944 strings.push_back (path);
947 if (getenv ("ARDOUR_WITH_JACK")) {
948 /* no other options - only use the JACK we supply */
949 if (strings.empty()) {
950 fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
951 /*NOTREACHED*/
953 return;
955 #else
956 string path;
957 #endif
959 PathScanner scanner;
960 vector<string *> *jack_servers;
961 std::map<string,int> un;
962 char *p;
963 bool need_minimal_path = false;
965 p = getenv ("PATH");
967 if (p && *p) {
968 path = p;
969 } else {
970 need_minimal_path = true;
973 #ifdef __APPLE__
974 // many mac users don't have PATH set up to include
975 // likely installed locations of JACK
976 need_minimal_path = true;
977 #endif
979 if (need_minimal_path) {
980 if (path.empty()) {
981 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
982 } else {
983 path += ":/usr/local/bin:/opt/local/bin";
987 #ifdef __APPLE__
988 // push it back into the environment so that auto-started JACK can find it.
989 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
990 setenv ("PATH", path.c_str(), 1);
991 #endif
993 jack_servers = scanner (path, jack_server_filter, 0, false, true);
995 vector<string *>::iterator iter;
997 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
998 string p = **iter;
1000 if (un[p]++ == 0) {
1001 strings.push_back(p);
1006 string
1007 EngineControl::get_device_name (const string& driver, const string& human_readable)
1009 vector<string>::iterator n;
1010 vector<string>::iterator i;
1012 if (human_readable.empty()) {
1013 /* this can happen if the user's .ardourrc file has a device name from
1014 another computer system in it
1016 MessageDialog msg (_("You need to choose an audio device first."));
1017 msg.run ();
1018 return string();
1021 if (backend_devs.empty()) {
1022 return human_readable;
1025 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1026 if (human_readable == (*i)) {
1027 return (*n);
1031 if (i == devices[driver].end()) {
1032 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1035 return string();
1038 XMLNode&
1039 EngineControl::get_state ()
1041 XMLNode* root = new XMLNode ("AudioSetup");
1042 XMLNode* child;
1043 Glib::ustring path;
1045 child = new XMLNode ("periods");
1046 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1047 root->add_child_nocopy (*child);
1049 child = new XMLNode ("priority");
1050 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1051 root->add_child_nocopy (*child);
1053 child = new XMLNode ("ports");
1054 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1055 root->add_child_nocopy (*child);
1057 child = new XMLNode ("inchannels");
1058 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1059 root->add_child_nocopy (*child);
1061 child = new XMLNode ("outchannels");
1062 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1063 root->add_child_nocopy (*child);
1065 child = new XMLNode ("inlatency");
1066 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1067 root->add_child_nocopy (*child);
1069 child = new XMLNode ("outlatency");
1070 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1071 root->add_child_nocopy (*child);
1073 child = new XMLNode ("realtime");
1074 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1075 root->add_child_nocopy (*child);
1077 child = new XMLNode ("nomemorylock");
1078 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1079 root->add_child_nocopy (*child);
1081 child = new XMLNode ("unlockmemory");
1082 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1083 root->add_child_nocopy (*child);
1085 child = new XMLNode ("softmode");
1086 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1087 root->add_child_nocopy (*child);
1089 child = new XMLNode ("force16bit");
1090 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1091 root->add_child_nocopy (*child);
1093 child = new XMLNode ("hwmonitor");
1094 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1095 root->add_child_nocopy (*child);
1097 child = new XMLNode ("hwmeter");
1098 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1099 root->add_child_nocopy (*child);
1101 child = new XMLNode ("verbose");
1102 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1103 root->add_child_nocopy (*child);
1105 child = new XMLNode ("samplerate");
1106 child->add_property ("val", sample_rate_combo.get_active_text());
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("periodsize");
1110 child->add_property ("val", period_size_combo.get_active_text());
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("serverpath");
1114 child->add_property ("val", serverpath_combo.get_active_text());
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("driver");
1118 child->add_property ("val", driver_combo.get_active_text());
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("interface");
1122 child->add_property ("val", interface_combo.get_active_text());
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("timeout");
1126 child->add_property ("val", timeout_combo.get_active_text());
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("dither");
1130 child->add_property ("val", dither_mode_combo.get_active_text());
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("audiomode");
1134 child->add_property ("val", audio_mode_combo.get_active_text());
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("inputdevice");
1138 child->add_property ("val", input_device_combo.get_active_text());
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("outputdevice");
1142 child->add_property ("val", output_device_combo.get_active_text());
1143 root->add_child_nocopy (*child);
1145 return *root;
1148 void
1149 EngineControl::set_state (const XMLNode& root)
1151 XMLNodeList clist;
1152 XMLNodeConstIterator citer;
1153 XMLNode* child;
1154 XMLProperty* prop = NULL;
1155 bool using_dummy = false;
1157 int val;
1158 string strval;
1160 if ( (child = root.child ("driver"))){
1161 prop = child->property("val");
1162 if (prop && (prop->value() == "Dummy") ) {
1163 using_dummy = true;
1167 clist = root.children();
1169 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1170 if ( prop && (prop->value() == "FFADO" ))
1171 continue;
1172 child = *citer;
1174 prop = child->property ("val");
1176 if (!prop || prop->value().empty()) {
1178 if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1179 continue;
1180 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1181 continue;
1184 strval = prop->value();
1186 /* adjustments/spinners */
1188 if (child->name() == "periods") {
1189 val = atoi (strval);
1190 periods_adjustment.set_value(val);
1191 } else if (child->name() == "priority") {
1192 val = atoi (strval);
1193 priority_adjustment.set_value(val);
1194 } else if (child->name() == "ports") {
1195 val = atoi (strval);
1196 ports_adjustment.set_value(val);
1197 } else if (child->name() == "inchannels") {
1198 val = atoi (strval);
1199 input_channels.set_value(val);
1200 } else if (child->name() == "outchannels") {
1201 val = atoi (strval);
1202 output_channels.set_value(val);
1203 } else if (child->name() == "inlatency") {
1204 val = atoi (strval);
1205 input_latency.set_value(val);
1206 } else if (child->name() == "outlatency") {
1207 val = atoi (strval);
1208 output_latency.set_value(val);
1211 /* buttons */
1213 else if (child->name() == "realtime") {
1214 val = atoi (strval);
1215 realtime_button.set_active(val);
1216 } else if (child->name() == "nomemorylock") {
1217 val = atoi (strval);
1218 no_memory_lock_button.set_active(val);
1219 } else if (child->name() == "unlockmemory") {
1220 val = atoi (strval);
1221 unlock_memory_button.set_active(val);
1222 } else if (child->name() == "softmode") {
1223 val = atoi (strval);
1224 soft_mode_button.set_active(val);
1225 } else if (child->name() == "force16bit") {
1226 val = atoi (strval);
1227 force16bit_button.set_active(val);
1228 } else if (child->name() == "hwmonitor") {
1229 val = atoi (strval);
1230 hw_monitor_button.set_active(val);
1231 } else if (child->name() == "hwmeter") {
1232 val = atoi (strval);
1233 hw_meter_button.set_active(val);
1234 } else if (child->name() == "verbose") {
1235 val = atoi (strval);
1236 verbose_output_button.set_active(val);
1239 /* combos */
1241 else if (child->name() == "samplerate") {
1242 sample_rate_combo.set_active_text(strval);
1243 } else if (child->name() == "periodsize") {
1244 period_size_combo.set_active_text(strval);
1245 } else if (child->name() == "serverpath") {
1246 /* do not allow us to use a server path that doesn't
1247 exist on this system. this handles cases where
1248 the user has an RC file listing a serverpath
1249 from some other machine.
1251 vector<string>::iterator x;
1252 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1253 if (*x == strval) {
1254 break;
1257 if (x != server_strings.end()) {
1258 serverpath_combo.set_active_text (strval);
1259 } else {
1260 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1261 strval)
1262 << endmsg;
1264 } else if (child->name() == "driver") {
1265 driver_combo.set_active_text(strval);
1266 } else if (child->name() == "interface") {
1267 interface_combo.set_active_text(strval);
1268 } else if (child->name() == "timeout") {
1269 timeout_combo.set_active_text(strval);
1270 } else if (child->name() == "dither") {
1271 dither_mode_combo.set_active_text(strval);
1272 } else if (child->name() == "audiomode") {
1273 audio_mode_combo.set_active_text(strval);
1274 } else if (child->name() == "inputdevice") {
1275 input_device_combo.set_active_text(strval);
1276 } else if (child->name() == "outputdevice") {
1277 output_device_combo.set_active_text(strval);