fix up file renaming code a little bit
[ArdourMidi.git] / gtk2_ardour / engine_dialog.cc
blob77f5c5facdc130d93c4901848ccde1779532f22a
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 (sigc::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 (sigc::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 (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
176 periods_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &EngineControl::redisplay_latency));
177 period_size_combo.signal_changed().connect (sigc::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 (sigc::mem_fun (*this, &EngineControl::start_engine));
202 stop_button.signal_clicked().connect (sigc::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.set_active (true);
219 realtime_button.signal_toggled().connect (sigc::mem_fun (*this, &EngineControl::realtime_changed));
220 realtime_changed ();
222 #if PROVIDE_TOO_MANY_OPTIONS
224 #ifndef __APPLE__
225 label = manage (new Label (_("Realtime Priority")));
226 label->set_alignment (1.0, 0.5);
227 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
228 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
229 ++row;
230 priority_spinner.set_value (60);
232 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
233 ++row;
234 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
235 ++row;
236 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
237 ++row;
238 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
239 ++row;
240 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
241 ++row;
242 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
243 ++row;
244 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
245 ++row;
246 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
247 ++row;
248 #else
249 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
250 ++row;
251 #endif
253 strings.clear ();
254 strings.push_back (_("Ignore"));
255 strings.push_back ("500 msec");
256 strings.push_back ("1 sec");
257 strings.push_back ("2 sec");
258 strings.push_back ("10 sec");
259 set_popdown_strings (timeout_combo, strings);
260 timeout_combo.set_active_text (strings.front ());
262 label = manage (new Label (_("Client timeout")));
263 label->set_alignment (1.0, 0.5);
264 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
265 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
266 ++row;
268 #endif /* PROVIDE_TOO_MANY_OPTIONS */
269 label = manage (new Label (_("Number of ports")));
270 label->set_alignment (1.0, 0.5);
271 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
272 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
273 ++row;
275 #ifndef __APPLE__
276 label = manage (new Label (_("Dither")));
277 label->set_alignment (1.0, 0.5);
278 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
279 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
280 ++row;
281 #endif
283 find_jack_servers (server_strings);
285 if (server_strings.empty()) {
286 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
287 /*NOTREACHED*/
290 set_popdown_strings (serverpath_combo, server_strings);
291 serverpath_combo.set_active_text (server_strings.front());
293 if (server_strings.size() > 1) {
294 label = manage (new Label (_("Server:")));
295 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
296 label->set_alignment (0.0, 0.5);
297 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
298 ++row;
301 /* device settings */
303 device_packer.set_spacings (6);
304 row = 0;
306 #ifndef __APPLE__
307 label = manage (new Label (_("Input device")));
308 label->set_alignment (1.0, 0.5);
309 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
310 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
311 ++row;
312 label = manage (new Label (_("Output device")));
313 label->set_alignment (1.0, 0.5);
314 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
315 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
316 ++row;
317 #endif
318 label = manage (new Label (_("Input channels")));
319 label->set_alignment (1.0, 0.5);
320 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
321 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
322 ++row;
323 label = manage (new Label (_("Output channels")));
324 label->set_alignment (1.0, 0.5);
325 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
326 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
327 ++row;
328 label = manage (new Label (_("Hardware input latency (samples)")));
329 label->set_alignment (1.0, 0.5);
330 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
331 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
332 ++row;
333 label = manage (new Label (_("Hardware output latency (samples)")));
334 label->set_alignment (1.0, 0.5);
335 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
336 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
337 ++row;
339 basic_hbox.pack_start (basic_packer, false, false);
340 options_hbox.pack_start (options_packer, false, false);
342 device_packer.set_border_width (12);
343 options_packer.set_border_width (12);
344 basic_packer.set_border_width (12);
346 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
347 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
348 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
349 notebook.set_border_width (12);
351 set_border_width (12);
352 pack_start (notebook);
355 EngineControl::~EngineControl ()
360 void
361 EngineControl::build_command_line (vector<string>& cmd)
363 string str;
364 string driver;
365 bool using_oss = false;
366 bool using_alsa = false;
367 bool using_coreaudio = false;
368 bool using_netjack = false;
369 bool using_ffado = false;
370 bool using_dummy = false;
372 /* first, path to jackd */
374 cmd.push_back (serverpath_combo.get_active_text ());
376 /* now jackd arguments */
378 str = timeout_combo.get_active_text ();
379 if (str != _("Ignore")) {
380 double secs = 0;
381 uint32_t msecs;
382 secs = atof (str);
383 msecs = (uint32_t) floor (secs * 1000.0);
384 if (msecs > 0) {
385 cmd.push_back ("-t");
386 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 snd_pcm_info_set_device (pcminfo, device);
775 snd_pcm_info_set_subdevice (pcminfo, 0);
776 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
778 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
779 devs.push_back (snd_pcm_info_get_name (pcminfo));
780 devname += ',';
781 devname += to_string (device, std::dec);
782 backend_devs.push_back (devname);
786 snd_ctl_close(handle);
790 return devs;
793 vector<string>
794 EngineControl::enumerate_ffado_devices ()
796 vector<string> devs;
797 backend_devs.clear ();
798 return devs;
801 vector<string>
802 EngineControl::enumerate_freebob_devices ()
804 vector<string> devs;
805 return devs;
807 vector<string>
808 EngineControl::enumerate_oss_devices ()
810 vector<string> devs;
811 return devs;
813 vector<string>
814 EngineControl::enumerate_dummy_devices ()
816 vector<string> devs;
817 return devs;
819 vector<string>
820 EngineControl::enumerate_netjack_devices ()
822 vector<string> devs;
823 return devs;
825 #endif
827 void
828 EngineControl::driver_changed ()
830 string driver = driver_combo.get_active_text();
831 string::size_type maxlen = 0;
832 int maxindex = -1;
833 int n = 0;
835 enumerate_devices (driver);
837 vector<string>& strings = devices[driver];
839 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
840 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
841 return;
844 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
845 if ((*i).length() > maxlen) {
846 maxlen = (*i).length();
847 maxindex = n;
851 set_popdown_strings (interface_combo, strings);
852 set_popdown_strings (input_device_combo, strings);
853 set_popdown_strings (output_device_combo, strings);
855 if (!strings.empty()) {
856 interface_combo.set_active_text (strings.front());
857 input_device_combo.set_active_text (strings.front());
858 output_device_combo.set_active_text (strings.front());
861 if (driver == "ALSA") {
862 soft_mode_button.set_sensitive (true);
863 force16bit_button.set_sensitive (true);
864 hw_monitor_button.set_sensitive (true);
865 hw_meter_button.set_sensitive (true);
866 monitor_button.set_sensitive (true);
867 } else {
868 soft_mode_button.set_sensitive (false);
869 force16bit_button.set_sensitive (false);
870 hw_monitor_button.set_sensitive (false);
871 hw_meter_button.set_sensitive (false);
872 monitor_button.set_sensitive (false);
876 uint32_t
877 EngineControl::get_rate ()
879 return atoi (sample_rate_combo.get_active_text ());
882 void
883 EngineControl::redisplay_latency ()
885 uint32_t rate = get_rate();
886 #ifdef __APPLE_
887 float periods = 2;
888 #else
889 float periods = periods_adjustment.get_value();
890 #endif
891 float period_size = atof (period_size_combo.get_active_text());
893 char buf[32];
894 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
896 latency_label.set_text (buf);
899 void
900 EngineControl::audio_mode_changed ()
902 Glib::ustring str = audio_mode_combo.get_active_text();
904 if (str == _("Playback/Recording on 1 Device")) {
905 input_device_combo.set_sensitive (false);
906 output_device_combo.set_sensitive (false);
907 } else if (str == _("Playback/Recording on 2 Devices")) {
908 input_device_combo.set_sensitive (true);
909 output_device_combo.set_sensitive (true);
910 } else if (str == _("Playback only")) {
911 output_device_combo.set_sensitive (true);
912 } else if (str == _("Recording only")) {
913 input_device_combo.set_sensitive (true);
917 static bool jack_server_filter(const string& str, void */*arg*/)
919 return str == "jackd" || str == "jackdmp";
922 void
923 EngineControl::find_jack_servers (vector<string>& strings)
925 #ifdef __APPLE__
926 /* this magic lets us finds the path to the OSX bundle, and then
927 we infer JACK's location from there
930 char execpath[MAXPATHLEN+1];
931 uint32_t pathsz = sizeof (execpath);
933 _NSGetExecutablePath (execpath, &pathsz);
935 string path (Glib::path_get_dirname (execpath));
936 path += "/jackd";
938 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
939 strings.push_back (path);
942 if (getenv ("ARDOUR_WITH_JACK")) {
943 /* no other options - only use the JACK we supply */
944 if (strings.empty()) {
945 fatal << string_compose (_("JACK appears to be missing from the %1 bundle"), PROGRAM_NAME) << endmsg;
946 /*NOTREACHED*/
948 return;
950 #else
951 string path;
952 #endif
954 PathScanner scanner;
955 vector<string *> *jack_servers;
956 std::map<string,int> un;
957 char *p;
958 bool need_minimal_path = false;
960 p = getenv ("PATH");
962 if (p && *p) {
963 path = p;
964 } else {
965 need_minimal_path = true;
968 #ifdef __APPLE__
969 // many mac users don't have PATH set up to include
970 // likely installed locations of JACK
971 need_minimal_path = true;
972 #endif
974 if (need_minimal_path) {
975 if (path.empty()) {
976 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
977 } else {
978 path += ":/usr/local/bin:/opt/local/bin";
982 #ifdef __APPLE__
983 // push it back into the environment so that auto-started JACK can find it.
984 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
985 setenv ("PATH", path.c_str(), 1);
986 #endif
988 jack_servers = scanner (path, jack_server_filter, 0, false, true);
990 vector<string *>::iterator iter;
992 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
993 string p = **iter;
995 if (un[p]++ == 0) {
996 strings.push_back(p);
1002 string
1003 EngineControl::get_device_name (const string& driver, const string& human_readable)
1005 vector<string>::iterator n;
1006 vector<string>::iterator i;
1008 if (human_readable.empty()) {
1009 /* this can happen if the user's .ardourrc file has a device name from
1010 another computer system in it
1012 MessageDialog msg (_("You need to choose an audio device first."));
1013 msg.run ();
1014 return string();
1017 if (backend_devs.empty()) {
1018 return human_readable;
1021 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1022 if (human_readable == (*i)) {
1023 return (*n);
1027 if (i == devices[driver].end()) {
1028 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1031 return string();
1034 XMLNode&
1035 EngineControl::get_state ()
1037 XMLNode* root = new XMLNode ("AudioSetup");
1038 XMLNode* child;
1039 Glib::ustring path;
1041 child = new XMLNode ("periods");
1042 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1043 root->add_child_nocopy (*child);
1045 child = new XMLNode ("priority");
1046 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1047 root->add_child_nocopy (*child);
1049 child = new XMLNode ("ports");
1050 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1051 root->add_child_nocopy (*child);
1053 child = new XMLNode ("inchannels");
1054 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1055 root->add_child_nocopy (*child);
1057 child = new XMLNode ("outchannels");
1058 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1059 root->add_child_nocopy (*child);
1061 child = new XMLNode ("inlatency");
1062 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1063 root->add_child_nocopy (*child);
1065 child = new XMLNode ("outlatency");
1066 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1067 root->add_child_nocopy (*child);
1069 child = new XMLNode ("realtime");
1070 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1071 root->add_child_nocopy (*child);
1073 child = new XMLNode ("nomemorylock");
1074 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1075 root->add_child_nocopy (*child);
1077 child = new XMLNode ("unlockmemory");
1078 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1079 root->add_child_nocopy (*child);
1081 child = new XMLNode ("softmode");
1082 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1083 root->add_child_nocopy (*child);
1085 child = new XMLNode ("force16bit");
1086 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1087 root->add_child_nocopy (*child);
1089 child = new XMLNode ("hwmonitor");
1090 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1091 root->add_child_nocopy (*child);
1093 child = new XMLNode ("hwmeter");
1094 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1095 root->add_child_nocopy (*child);
1097 child = new XMLNode ("verbose");
1098 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1099 root->add_child_nocopy (*child);
1101 child = new XMLNode ("samplerate");
1102 child->add_property ("val", sample_rate_combo.get_active_text());
1103 root->add_child_nocopy (*child);
1105 child = new XMLNode ("periodsize");
1106 child->add_property ("val", period_size_combo.get_active_text());
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("serverpath");
1110 child->add_property ("val", serverpath_combo.get_active_text());
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("driver");
1114 child->add_property ("val", driver_combo.get_active_text());
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("interface");
1118 child->add_property ("val", interface_combo.get_active_text());
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("timeout");
1122 child->add_property ("val", timeout_combo.get_active_text());
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("dither");
1126 child->add_property ("val", dither_mode_combo.get_active_text());
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("audiomode");
1130 child->add_property ("val", audio_mode_combo.get_active_text());
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("inputdevice");
1134 child->add_property ("val", input_device_combo.get_active_text());
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("outputdevice");
1138 child->add_property ("val", output_device_combo.get_active_text());
1139 root->add_child_nocopy (*child);
1141 return *root;
1144 void
1145 EngineControl::set_state (const XMLNode& root)
1147 XMLNodeList clist;
1148 XMLNodeConstIterator citer;
1149 XMLNode* child;
1150 XMLProperty* prop = NULL;
1151 bool using_dummy = false;
1153 int val;
1154 string strval;
1156 if ( (child = root.child ("driver"))){
1157 prop = child->property("val");
1158 if (prop && (prop->value() == "Dummy") ) {
1159 using_dummy = true;
1163 clist = root.children();
1165 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1167 child = *citer;
1169 prop = child->property ("val");
1171 if (!prop || prop->value().empty()) {
1173 if ((using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) ||
1174 child->name() == "timeout")
1175 continue;
1176 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1177 continue;
1180 strval = prop->value();
1182 /* adjustments/spinners */
1184 if (child->name() == "periods") {
1185 val = atoi (strval);
1186 periods_adjustment.set_value(val);
1187 } else if (child->name() == "priority") {
1188 val = atoi (strval);
1189 priority_adjustment.set_value(val);
1190 } else if (child->name() == "ports") {
1191 val = atoi (strval);
1192 ports_adjustment.set_value(val);
1193 } else if (child->name() == "inchannels") {
1194 val = atoi (strval);
1195 input_channels.set_value(val);
1196 } else if (child->name() == "outchannels") {
1197 val = atoi (strval);
1198 output_channels.set_value(val);
1199 } else if (child->name() == "inlatency") {
1200 val = atoi (strval);
1201 input_latency.set_value(val);
1202 } else if (child->name() == "outlatency") {
1203 val = atoi (strval);
1204 output_latency.set_value(val);
1207 /* buttons */
1209 else if (child->name() == "realtime") {
1210 val = atoi (strval);
1211 realtime_button.set_active(val);
1212 } else if (child->name() == "nomemorylock") {
1213 val = atoi (strval);
1214 no_memory_lock_button.set_active(val);
1215 } else if (child->name() == "unlockmemory") {
1216 val = atoi (strval);
1217 unlock_memory_button.set_active(val);
1218 } else if (child->name() == "softmode") {
1219 val = atoi (strval);
1220 soft_mode_button.set_active(val);
1221 } else if (child->name() == "force16bit") {
1222 val = atoi (strval);
1223 force16bit_button.set_active(val);
1224 } else if (child->name() == "hwmonitor") {
1225 val = atoi (strval);
1226 hw_monitor_button.set_active(val);
1227 } else if (child->name() == "hwmeter") {
1228 val = atoi (strval);
1229 hw_meter_button.set_active(val);
1230 } else if (child->name() == "verbose") {
1231 val = atoi (strval);
1232 verbose_output_button.set_active(val);
1235 /* combos */
1237 else if (child->name() == "samplerate") {
1238 sample_rate_combo.set_active_text(strval);
1239 } else if (child->name() == "periodsize") {
1240 period_size_combo.set_active_text(strval);
1241 } else if (child->name() == "serverpath") {
1243 /* only attempt to set this if we have bothered to look
1244 up server names already. otherwise this is all
1245 redundant (actually, all of this dialog/widget
1246 is redundant in that case ...)
1249 if (!server_strings.empty()) {
1250 /* do not allow us to use a server path that doesn't
1251 exist on this system. this handles cases where
1252 the user has an RC file listing a serverpath
1253 from some other machine.
1255 vector<string>::iterator x;
1256 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1257 if (*x == strval) {
1258 break;
1261 if (x != server_strings.end()) {
1262 serverpath_combo.set_active_text (strval);
1263 } else {
1264 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1265 strval)
1266 << endmsg;
1270 } else if (child->name() == "driver") {
1271 driver_combo.set_active_text(strval);
1272 } else if (child->name() == "interface") {
1273 interface_combo.set_active_text(strval);
1274 } else if (child->name() == "timeout") {
1275 timeout_combo.set_active_text(strval);
1276 } else if (child->name() == "dither") {
1277 dither_mode_combo.set_active_text(strval);
1278 } else if (child->name() == "audiomode") {
1279 audio_mode_combo.set_active_text(strval);
1280 } else if (child->name() == "inputdevice") {
1281 input_device_combo.set_active_text(strval);
1282 } else if (child->name() == "outputdevice") {
1283 output_device_combo.set_active_text(strval);