fix import/embed with "sequence files" option
[ardour2.git] / gtk2_ardour / engine_dialog.cc
blobf1d23c7cdbb7e4944642d2b5ea7c037b43db9441
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;
76 _interface_chosen = false;
78 strings.push_back (_("8000Hz"));
79 strings.push_back (_("22050Hz"));
80 strings.push_back (_("44100Hz"));
81 strings.push_back (_("48000Hz"));
82 strings.push_back (_("88200Hz"));
83 strings.push_back (_("96000Hz"));
84 strings.push_back (_("192000Hz"));
85 set_popdown_strings (sample_rate_combo, strings);
86 sample_rate_combo.set_active_text ("48000Hz");
88 strings.clear ();
89 strings.push_back ("32");
90 strings.push_back ("64");
91 strings.push_back ("128");
92 strings.push_back ("256");
93 strings.push_back ("512");
94 strings.push_back ("1024");
95 strings.push_back ("2048");
96 strings.push_back ("4096");
97 strings.push_back ("8192");
98 set_popdown_strings (period_size_combo, strings);
99 period_size_combo.set_active_text ("1024");
101 strings.clear ();
102 strings.push_back (_("None"));
103 strings.push_back (_("Triangular"));
104 strings.push_back (_("Rectangular"));
105 strings.push_back (_("Shaped"));
106 set_popdown_strings (dither_mode_combo, strings);
107 dither_mode_combo.set_active_text (_("None"));
109 /* basic parameters */
111 basic_packer.set_spacings (6);
113 strings.clear ();
114 #ifdef __APPLE__
115 strings.push_back (X_("CoreAudio"));
116 #else
117 strings.push_back (X_("ALSA"));
118 strings.push_back (X_("OSS"));
119 strings.push_back (X_("FFADO"));
120 #endif
121 strings.push_back (X_("NetJACK"));
122 strings.push_back (X_("Dummy"));
123 set_popdown_strings (driver_combo, strings);
124 driver_combo.set_active_text (strings.front());
126 driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed));
127 driver_changed ();
129 strings.clear ();
130 strings.push_back (_("Playback/Recording on 1 Device"));
131 strings.push_back (_("Playback/Recording on 2 Devices"));
132 strings.push_back (_("Playback only"));
133 strings.push_back (_("Recording only"));
134 set_popdown_strings (audio_mode_combo, strings);
135 audio_mode_combo.set_active_text (strings.front());
137 audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed));
138 audio_mode_changed ();
140 row = 0;
142 label = manage (new Label (_("Driver")));
143 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
144 basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
145 row++;
147 label = manage (new Label (_("Interface")));
148 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
149 basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
150 row++;
152 label = manage (new Label (_("Sample Rate")));
153 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
154 basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
155 row++;
157 label = manage (new Label (_("Buffer size")));
158 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
159 basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
160 row++;
162 #ifndef __APPLE__
163 label = manage (new Label (_("Number of buffers")));
164 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
165 basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
166 periods_spinner.set_value (2);
167 row++;
168 #endif
170 label = manage (new Label (_("Approximate latency")));
171 label->set_alignment (0.0, 0.5);
172 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
173 basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
174 row++;
176 sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
177 periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
178 period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency));
179 redisplay_latency();
180 row++;
181 /* no audio mode with CoreAudio, its duplex or nuthin' */
183 #ifndef __APPLE__
184 label = manage (new Label (_("Audio Mode")));
185 basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
186 basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
187 row++;
188 #endif
190 interface_combo.set_size_request (125, -1);
191 input_device_combo.set_size_request (125, -1);
192 output_device_combo.set_size_request (125, -1);
196 if (engine_running()) {
197 start_button.set_sensitive (false);
198 } else {
199 stop_button.set_sensitive (false);
202 start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
203 stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine));
206 button_box.pack_start (start_button, false, false);
207 button_box.pack_start (stop_button, false, false);
209 // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0);
211 /* options */
213 options_packer.set_spacings (6);
214 row = 0;
216 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
217 ++row;
219 realtime_button.set_active (true); /* RT is active by default */
220 realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
221 realtime_changed ();
223 #ifndef __APPLE__
224 label = manage (new Label (_("Realtime Priority")));
225 label->set_alignment (1.0, 0.5);
226 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
227 options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
228 ++row;
229 priority_spinner.set_value (60);
231 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
232 ++row;
233 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
234 ++row;
235 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
236 ++row;
237 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
238 ++row;
239 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
240 ++row;
241 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
242 ++row;
243 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
244 ++row;
245 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
246 ++row;
247 #else
248 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
249 ++row;
250 #endif
252 strings.clear ();
253 strings.push_back (_("Ignore"));
254 strings.push_back ("500 msec");
255 strings.push_back ("1 sec");
256 strings.push_back ("2 sec");
257 strings.push_back ("10 sec");
258 set_popdown_strings (timeout_combo, strings);
259 timeout_combo.set_active_text (strings.front ());
261 label = manage (new Label (_("Client timeout")));
262 label->set_alignment (1.0, 0.5);
263 options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
264 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
265 ++row;
267 label = manage (new Label (_("Number of ports")));
268 label->set_alignment (1.0, 0.5);
269 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
270 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
271 ++row;
273 #ifndef __APPLE__
274 label = manage (new Label (_("Dither")));
275 label->set_alignment (1.0, 0.5);
276 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
277 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
278 ++row;
279 #endif
281 /* defer server stuff till later */
282 server_row = row++;
284 /* device settings */
286 device_packer.set_spacings (6);
287 row = 0;
289 #ifndef __APPLE__
290 label = manage (new Label (_("Input device")));
291 label->set_alignment (1.0, 0.5);
292 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
293 device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
294 ++row;
295 label = manage (new Label (_("Output device")));
296 label->set_alignment (1.0, 0.5);
297 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
298 device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
299 ++row;
300 #endif
301 label = manage (new Label (_("Input channels")));
302 label->set_alignment (1.0, 0.5);
303 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
304 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
305 ++row;
306 label = manage (new Label (_("Output channels")));
307 label->set_alignment (1.0, 0.5);
308 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
309 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
310 ++row;
311 label = manage (new Label (_("Hardware input latency (samples)")));
312 label->set_alignment (1.0, 0.5);
313 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
314 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
315 ++row;
316 label = manage (new Label (_("Hardware output latency (samples)")));
317 label->set_alignment (1.0, 0.5);
318 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
319 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
320 ++row;
322 basic_hbox.pack_start (basic_packer, false, false);
323 options_hbox.pack_start (options_packer, false, false);
325 device_packer.set_border_width (12);
326 options_packer.set_border_width (12);
327 basic_packer.set_border_width (12);
329 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
330 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
331 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
332 notebook.set_border_width (12);
334 set_border_width (12);
335 pack_start (notebook);
338 EngineControl::~EngineControl ()
343 void
344 EngineControl::discover_servers ()
346 find_jack_servers (server_strings);
348 if (server_strings.empty()) {
349 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
350 /*NOTREACHED*/
353 set_popdown_strings (serverpath_combo, server_strings);
354 serverpath_combo.set_active_text (server_strings.front());
356 if (server_strings.size() > 1) {
357 Gtk::Label* label = manage (new Label (_("Server:")));
358 options_packer.attach (*label, 0, 1, server_row, server_row + 1, FILL|EXPAND, (AttachOptions) 0);
359 label->set_alignment (0.0, 0.5);
360 options_packer.attach (serverpath_combo, 1, 2, server_row, server_row + 1, FILL|EXPAND, (AttachOptions) 0);
364 void
365 EngineControl::build_command_line (vector<string>& cmd)
367 string str;
368 string driver;
369 bool using_oss = false;
370 bool using_alsa = false;
371 bool using_coreaudio = false;
372 bool using_netjack = false;
373 bool using_ffado = false;
374 bool using_dummy = false;
376 /* first, path to jackd */
378 cmd.push_back (serverpath_combo.get_active_text ());
380 /* now jackd arguments */
382 str = timeout_combo.get_active_text ();
383 if (str != _("Ignore")) {
384 double secs;
385 uint32_t msecs;
386 secs = atof (str);
387 msecs = (uint32_t) floor (secs * 1000.0);
388 cmd.push_back ("-t");
389 cmd.push_back (to_string (msecs, std::dec));
392 if (no_memory_lock_button.get_active()) {
393 cmd.push_back ("-m"); /* no munlock */
396 cmd.push_back ("-p"); /* port max */
397 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
399 if (realtime_button.get_active()) {
400 cmd.push_back ("-R");
401 cmd.push_back ("-P");
402 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
405 if (unlock_memory_button.get_active()) {
406 cmd.push_back ("-u");
409 if (verbose_output_button.get_active()) {
410 cmd.push_back ("-v");
413 /* now add fixed arguments (not user-selectable) */
415 cmd.push_back ("-T"); // temporary */
417 /* next the driver */
419 cmd.push_back ("-d");
421 driver = driver_combo.get_active_text ();
422 if (driver == X_("ALSA")) {
423 using_alsa = true;
424 cmd.push_back ("alsa");
425 } else if (driver == X_("OSS")) {
426 using_oss = true;
427 cmd.push_back ("oss");
428 } else if (driver == X_("CoreAudio")) {
429 using_coreaudio = true;
430 cmd.push_back ("coreaudio");
431 } else if (driver == X_("NetJACK")) {
432 using_netjack = true;
433 cmd.push_back ("netjack");
434 } else if (driver == X_("FFADO")) {
435 using_ffado = true;
437 /* do this until FFADO becomes the standard */
439 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
441 if (hack) {
442 cmd.push_back (hack);
443 } else {
444 cmd.push_back ("freebob");
447 } else if ( driver == X_("Dummy")) {
448 using_dummy = true;
449 cmd.push_back ("dummy");
452 /* driver arguments */
454 if (!using_coreaudio) {
455 str = audio_mode_combo.get_active_text();
457 if (str == _("Playback/Recording on 1 Device")) {
459 /* relax */
461 } else if (str == _("Playback/Recording on 2 Devices")) {
463 string input_device = get_device_name (driver, input_device_combo.get_active_text());
464 string output_device = get_device_name (driver, output_device_combo.get_active_text());
466 if (input_device.empty() || output_device.empty()) {
467 cmd.clear ();
468 return;
471 cmd.push_back ("-C");
472 cmd.push_back (input_device);
473 cmd.push_back ("-P");
474 cmd.push_back (output_device);
476 } else if (str == _("Playback only")) {
477 cmd.push_back ("-P");
478 } else if (str == _("Recording only")) {
479 cmd.push_back ("-C");
482 if (! using_dummy ) {
483 cmd.push_back ("-n");
484 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
488 cmd.push_back ("-r");
489 cmd.push_back (to_string (get_rate(), std::dec));
491 cmd.push_back ("-p");
492 cmd.push_back (period_size_combo.get_active_text());
494 if (using_alsa) {
496 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
498 string device = get_device_name (driver, interface_combo.get_active_text());
499 if (device.empty()) {
500 cmd.clear ();
501 return;
504 cmd.push_back ("-d");
505 cmd.push_back (device);
506 _interface_chosen = true;
509 if (hw_meter_button.get_active()) {
510 cmd.push_back ("-M");
513 if (hw_monitor_button.get_active()) {
514 cmd.push_back ("-H");
517 str = dither_mode_combo.get_active_text();
519 if (str == _("None")) {
520 } else if (str == _("Triangular")) {
521 cmd.push_back ("-z triangular");
522 } else if (str == _("Rectangular")) {
523 cmd.push_back ("-z rectangular");
524 } else if (str == _("Shaped")) {
525 cmd.push_back ("-z shaped");
528 if (force16bit_button.get_active()) {
529 cmd.push_back ("-S");
532 if (soft_mode_button.get_active()) {
533 cmd.push_back ("-s");
536 } else if (using_coreaudio) {
538 #ifdef __APPLE__
539 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
541 string device = get_device_name (driver, interface_combo.get_active_text());
542 if (device.empty()) {
543 cmd.clear ();
544 return;
547 cmd.push_back ("-d");
548 cmd.push_back (device);
549 #endif
551 } else if (using_oss) {
553 } else if (using_netjack) {
558 bool
559 EngineControl::engine_running ()
561 jack_status_t status;
562 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
564 if (status == 0) {
565 jack_client_close (c);
566 return true;
568 return false;
572 EngineControl::setup_engine ()
574 vector<string> args;
575 std::string cwd = "/tmp";
577 build_command_line (args);
579 if (args.empty()) {
580 return 1; // try again
583 Glib::ustring jackdrc_path = Glib::get_home_dir();
584 jackdrc_path += "/.jackdrc";
586 ofstream jackdrc (jackdrc_path.c_str());
587 if (!jackdrc) {
588 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
589 return -1;
591 cerr << "JACK COMMAND: ";
592 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
593 cerr << (*i) << ' ';
594 jackdrc << (*i) << ' ';
596 cerr << endl;
597 jackdrc << endl;
598 jackdrc.close ();
600 _used = true;
602 return 0;
605 void
606 EngineControl::realtime_changed ()
608 #ifndef __APPLE__
609 priority_spinner.set_sensitive (realtime_button.get_active());
610 #endif
613 void
614 EngineControl::enumerate_devices (const string& driver)
616 /* note: case matters for the map keys */
618 if (driver == "CoreAudio") {
619 #ifdef __APPLE__
620 devices[driver] = enumerate_coreaudio_devices ();
621 #endif
623 #ifndef __APPLE__
624 } else if (driver == "ALSA") {
625 devices[driver] = enumerate_alsa_devices ();
626 } else if (driver == "FFADO") {
627 devices[driver] = enumerate_ffado_devices ();
628 } else if (driver == "OSS") {
629 devices[driver] = enumerate_oss_devices ();
630 } else if (driver == "Dummy") {
631 devices[driver] = enumerate_dummy_devices ();
632 } else if (driver == "NetJACK") {
633 devices[driver] = enumerate_netjack_devices ();
635 #else
637 #endif
640 #ifdef __APPLE__
641 static OSStatus
642 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
644 UInt32 size = sizeof(CFStringRef);
645 CFStringRef UI;
646 OSStatus res = AudioDeviceGetProperty(id, 0, false,
647 kAudioDevicePropertyDeviceUID, &size, &UI);
648 if (res == noErr)
649 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
650 CFRelease(UI);
651 return res;
654 vector<string>
655 EngineControl::enumerate_coreaudio_devices ()
657 vector<string> devs;
659 // Find out how many Core Audio devices are there, if any...
660 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
661 OSStatus err;
662 Boolean isWritable;
663 size_t outSize = sizeof(isWritable);
665 backend_devs.clear ();
667 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
668 &outSize, &isWritable);
669 if (err == noErr) {
670 // Calculate the number of device available...
671 int numCoreDevices = outSize / sizeof(AudioDeviceID);
672 // Make space for the devices we are about to get...
673 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
674 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
675 &outSize, (void *) coreDeviceIDs);
676 if (err == noErr) {
677 // Look for the CoreAudio device name...
678 char coreDeviceName[256];
679 size_t nameSize;
681 for (int i = 0; i < numCoreDevices; i++) {
683 nameSize = sizeof (coreDeviceName);
685 /* enforce duplex devices only */
687 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
688 0, true, kAudioDevicePropertyStreams,
689 &outSize, &isWritable);
691 if (err != noErr || outSize == 0) {
692 continue;
695 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
696 0, false, kAudioDevicePropertyStreams,
697 &outSize, &isWritable);
699 if (err != noErr || outSize == 0) {
700 continue;
703 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
704 0, true, kAudioDevicePropertyDeviceName,
705 &outSize, &isWritable);
706 if (err == noErr) {
707 err = AudioDeviceGetProperty(coreDeviceIDs[i],
708 0, true, kAudioDevicePropertyDeviceName,
709 &nameSize, (void *) coreDeviceName);
710 if (err == noErr) {
711 char drivername[128];
713 // this returns the unique id for the device
714 // that must be used on the commandline for jack
716 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
717 devs.push_back (coreDeviceName);
718 backend_devs.push_back (drivername);
724 delete [] coreDeviceIDs;
728 if (devs.size() == 0) {
729 MessageDialog msg (_("\
730 You do not have any audio devices capable of\n\
731 simultaneous playback and recording.\n\n\
732 Please use Applications -> Utilities -> Audio MIDI Setup\n\
733 to create an \"aggregrate\" device, or install a suitable\n\
734 audio interface.\n\n\
735 Please send email to Apple and ask them why new Macs\n\
736 have no duplex audio device.\n\n\
737 Alternatively, if you really want just playback\n\
738 or recording but not both, start JACK before running\n\
739 Ardour and choose the relevant device then."
741 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
742 msg.set_title (_("No suitable audio devices"));
743 msg.set_position (Gtk::WIN_POS_MOUSE);
744 msg.run ();
745 exit (1);
749 return devs;
751 #else
752 vector<string>
753 EngineControl::enumerate_alsa_devices ()
755 vector<string> devs;
757 snd_ctl_t *handle;
758 snd_ctl_card_info_t *info;
759 snd_pcm_info_t *pcminfo;
760 snd_ctl_card_info_alloca(&info);
761 snd_pcm_info_alloca(&pcminfo);
762 string devname;
763 int cardnum = -1;
764 int device = -1;
766 backend_devs.clear ();
768 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
770 devname = "hw:";
771 devname += to_string (cardnum, std::dec);
773 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
775 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
777 bool have_playback = false;
778 bool have_capture = false;
780 /* find duplex devices only */
782 snd_pcm_info_set_device (pcminfo, device);
783 snd_pcm_info_set_subdevice (pcminfo, 0);
784 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
786 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
787 have_capture = true;
790 snd_pcm_info_set_device (pcminfo, device);
791 snd_pcm_info_set_subdevice (pcminfo, 0);
792 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
794 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
795 have_playback = true;
798 if (have_capture && have_playback) {
799 devs.push_back (snd_pcm_info_get_name (pcminfo));
800 devname += ',';
801 devname += to_string (device, std::dec);
802 backend_devs.push_back (devname);
806 snd_ctl_close(handle);
810 return devs;
813 vector<string>
814 EngineControl::enumerate_ffado_devices ()
816 vector<string> devs;
817 backend_devs.clear ();
818 return devs;
821 vector<string>
822 EngineControl::enumerate_oss_devices ()
824 vector<string> devs;
825 return devs;
827 vector<string>
828 EngineControl::enumerate_dummy_devices ()
830 vector<string> devs;
831 return devs;
833 vector<string>
834 EngineControl::enumerate_netjack_devices ()
836 vector<string> devs;
837 return devs;
839 #endif
841 void
842 EngineControl::driver_changed ()
844 string driver = driver_combo.get_active_text();
845 string::size_type maxlen = 0;
846 int maxindex = -1;
847 int n = 0;
849 enumerate_devices (driver);
851 vector<string>& strings = devices[driver];
853 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
854 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
855 return;
858 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
859 if ((*i).length() > maxlen) {
860 maxlen = (*i).length();
861 maxindex = n;
865 set_popdown_strings (interface_combo, strings);
866 set_popdown_strings (input_device_combo, strings);
867 set_popdown_strings (output_device_combo, strings);
869 if (!strings.empty()) {
870 interface_combo.set_active_text (strings.front());
871 input_device_combo.set_active_text (strings.front());
872 output_device_combo.set_active_text (strings.front());
875 if (driver == "ALSA") {
876 soft_mode_button.set_sensitive (true);
877 force16bit_button.set_sensitive (true);
878 hw_monitor_button.set_sensitive (true);
879 hw_meter_button.set_sensitive (true);
880 monitor_button.set_sensitive (true);
881 } else {
882 soft_mode_button.set_sensitive (false);
883 force16bit_button.set_sensitive (false);
884 hw_monitor_button.set_sensitive (false);
885 hw_meter_button.set_sensitive (false);
886 monitor_button.set_sensitive (false);
890 uint32_t
891 EngineControl::get_rate ()
893 return atoi (sample_rate_combo.get_active_text ());
896 void
897 EngineControl::redisplay_latency ()
899 uint32_t rate = get_rate();
900 #ifdef __APPLE_
901 float periods = 2;
902 #else
903 float periods = periods_adjustment.get_value();
904 #endif
905 float period_size = atof (period_size_combo.get_active_text());
907 char buf[32];
908 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
910 latency_label.set_text (buf);
913 void
914 EngineControl::audio_mode_changed ()
916 Glib::ustring str = audio_mode_combo.get_active_text();
918 if (str == _("Playback/Recording on 1 Device")) {
919 input_device_combo.set_sensitive (false);
920 output_device_combo.set_sensitive (false);
921 } else if (str == _("Playback/Recording on 2 Devices")) {
922 input_device_combo.set_sensitive (true);
923 output_device_combo.set_sensitive (true);
924 } else if (str == _("Playback only")) {
925 output_device_combo.set_sensitive (true);
926 } else if (str == _("Recording only")) {
927 input_device_combo.set_sensitive (true);
931 static bool jack_server_filter(const string& str, void *arg)
933 return str == "jackd" || str == "jackdmp";
936 void
937 EngineControl::find_jack_servers (vector<string>& strings)
939 #ifdef __APPLE__
940 /* this magic lets us finds the path to the OSX bundle, and then
941 we infer JACK's location from there
944 char execpath[MAXPATHLEN+1];
945 uint32_t pathsz = sizeof (execpath);
947 _NSGetExecutablePath (execpath, &pathsz);
949 string path (Glib::path_get_dirname (execpath));
950 path += "/jackd";
952 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
953 strings.push_back (path);
956 if (getenv ("ARDOUR_WITH_JACK")) {
957 /* no other options - only use the JACK we supply */
958 if (strings.empty()) {
959 fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
960 /*NOTREACHED*/
962 return;
964 #else
965 string path;
966 #endif
968 PathScanner scanner;
969 vector<string *> *jack_servers;
970 std::map<string,int> un;
971 char *p;
972 bool need_minimal_path = false;
974 p = getenv ("PATH");
976 if (p && *p) {
977 path = p;
978 } else {
979 need_minimal_path = true;
982 #ifdef __APPLE__
983 // many mac users don't have PATH set up to include
984 // likely installed locations of JACK
985 need_minimal_path = true;
986 #endif
988 if (need_minimal_path) {
989 if (path.empty()) {
990 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
991 } else {
992 path += ":/usr/local/bin:/opt/local/bin";
996 #ifdef __APPLE__
997 // push it back into the environment so that auto-started JACK can find it.
998 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
999 setenv ("PATH", path.c_str(), 1);
1000 #endif
1002 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1004 vector<string *>::iterator iter;
1006 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1007 string p = **iter;
1009 if (un[p]++ == 0) {
1010 strings.push_back(p);
1015 string
1016 EngineControl::get_device_name (const string& driver, const string& human_readable)
1018 vector<string>::iterator n;
1019 vector<string>::iterator i;
1021 if (human_readable.empty()) {
1022 /* this can happen if the user's .ardourrc file has a device name from
1023 another computer system in it
1025 MessageDialog msg (_("You need to choose an audio device first."));
1026 msg.run ();
1027 return string();
1030 if (backend_devs.empty()) {
1031 return human_readable;
1034 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1035 if (human_readable == (*i)) {
1036 return (*n);
1040 if (i == devices[driver].end()) {
1041 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1044 return string();
1047 XMLNode&
1048 EngineControl::get_state ()
1050 XMLNode* root = new XMLNode ("AudioSetup");
1051 XMLNode* child;
1052 Glib::ustring path;
1054 child = new XMLNode ("periods");
1055 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1056 root->add_child_nocopy (*child);
1058 child = new XMLNode ("priority");
1059 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1060 root->add_child_nocopy (*child);
1062 child = new XMLNode ("ports");
1063 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1064 root->add_child_nocopy (*child);
1066 child = new XMLNode ("inchannels");
1067 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1068 root->add_child_nocopy (*child);
1070 child = new XMLNode ("outchannels");
1071 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1072 root->add_child_nocopy (*child);
1074 child = new XMLNode ("inlatency");
1075 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1076 root->add_child_nocopy (*child);
1078 child = new XMLNode ("outlatency");
1079 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1080 root->add_child_nocopy (*child);
1082 child = new XMLNode ("realtime");
1083 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1084 root->add_child_nocopy (*child);
1086 child = new XMLNode ("nomemorylock");
1087 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1088 root->add_child_nocopy (*child);
1090 child = new XMLNode ("unlockmemory");
1091 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1092 root->add_child_nocopy (*child);
1094 child = new XMLNode ("softmode");
1095 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1096 root->add_child_nocopy (*child);
1098 child = new XMLNode ("force16bit");
1099 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1100 root->add_child_nocopy (*child);
1102 child = new XMLNode ("hwmonitor");
1103 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1104 root->add_child_nocopy (*child);
1106 child = new XMLNode ("hwmeter");
1107 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1108 root->add_child_nocopy (*child);
1110 child = new XMLNode ("verbose");
1111 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1112 root->add_child_nocopy (*child);
1114 child = new XMLNode ("samplerate");
1115 child->add_property ("val", sample_rate_combo.get_active_text());
1116 root->add_child_nocopy (*child);
1118 child = new XMLNode ("periodsize");
1119 child->add_property ("val", period_size_combo.get_active_text());
1120 root->add_child_nocopy (*child);
1122 child = new XMLNode ("serverpath");
1123 child->add_property ("val", serverpath_combo.get_active_text());
1124 root->add_child_nocopy (*child);
1126 child = new XMLNode ("driver");
1127 child->add_property ("val", driver_combo.get_active_text());
1128 root->add_child_nocopy (*child);
1130 child = new XMLNode ("interface");
1131 child->add_property ("val", interface_combo.get_active_text());
1132 root->add_child_nocopy (*child);
1134 child = new XMLNode ("timeout");
1135 child->add_property ("val", timeout_combo.get_active_text());
1136 root->add_child_nocopy (*child);
1138 child = new XMLNode ("dither");
1139 child->add_property ("val", dither_mode_combo.get_active_text());
1140 root->add_child_nocopy (*child);
1142 child = new XMLNode ("audiomode");
1143 child->add_property ("val", audio_mode_combo.get_active_text());
1144 root->add_child_nocopy (*child);
1146 child = new XMLNode ("inputdevice");
1147 child->add_property ("val", input_device_combo.get_active_text());
1148 root->add_child_nocopy (*child);
1150 child = new XMLNode ("outputdevice");
1151 child->add_property ("val", output_device_combo.get_active_text());
1152 root->add_child_nocopy (*child);
1154 return *root;
1157 void
1158 EngineControl::set_state (const XMLNode& root)
1160 XMLNodeList clist;
1161 XMLNodeConstIterator citer;
1162 XMLNode* child;
1163 XMLProperty* prop = NULL;
1164 bool using_dummy = false;
1166 int val;
1167 string strval;
1169 if ( (child = root.child ("driver"))){
1170 prop = child->property("val");
1171 if (prop && (prop->value() == "Dummy") ) {
1172 using_dummy = true;
1176 clist = root.children();
1178 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1180 child = *citer;
1182 prop = child->property ("val");
1184 if (!prop || prop->value().empty()) {
1186 if (using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" )) {
1187 continue;
1190 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1191 continue;
1194 strval = prop->value();
1196 /* adjustments/spinners */
1198 if (child->name() == "periods") {
1199 val = atoi (strval);
1200 periods_adjustment.set_value(val);
1201 } else if (child->name() == "priority") {
1202 val = atoi (strval);
1203 priority_adjustment.set_value(val);
1204 } else if (child->name() == "ports") {
1205 val = atoi (strval);
1206 ports_adjustment.set_value(val);
1207 } else if (child->name() == "inchannels") {
1208 val = atoi (strval);
1209 input_channels.set_value(val);
1210 } else if (child->name() == "outchannels") {
1211 val = atoi (strval);
1212 output_channels.set_value(val);
1213 } else if (child->name() == "inlatency") {
1214 val = atoi (strval);
1215 input_latency.set_value(val);
1216 } else if (child->name() == "outlatency") {
1217 val = atoi (strval);
1218 output_latency.set_value(val);
1221 /* buttons */
1223 else if (child->name() == "realtime") {
1224 val = atoi (strval);
1225 realtime_button.set_active(val);
1226 } else if (child->name() == "nomemorylock") {
1227 val = atoi (strval);
1228 no_memory_lock_button.set_active(val);
1229 } else if (child->name() == "unlockmemory") {
1230 val = atoi (strval);
1231 unlock_memory_button.set_active(val);
1232 } else if (child->name() == "softmode") {
1233 val = atoi (strval);
1234 soft_mode_button.set_active(val);
1235 } else if (child->name() == "force16bit") {
1236 val = atoi (strval);
1237 force16bit_button.set_active(val);
1238 } else if (child->name() == "hwmonitor") {
1239 val = atoi (strval);
1240 hw_monitor_button.set_active(val);
1241 } else if (child->name() == "hwmeter") {
1242 val = atoi (strval);
1243 hw_meter_button.set_active(val);
1244 } else if (child->name() == "verbose") {
1245 val = atoi (strval);
1246 verbose_output_button.set_active(val);
1249 /* combos */
1251 else if (child->name() == "samplerate") {
1252 sample_rate_combo.set_active_text(strval);
1253 } else if (child->name() == "periodsize") {
1254 period_size_combo.set_active_text(strval);
1255 } else if (child->name() == "serverpath") {
1257 /* only attempt to set this if we have bothered to look
1258 up server names already. otherwise this is all
1259 redundant (actually, all of this dialog/widget
1260 is redundant in that case ...)
1264 if (!server_strings.empty()) {
1265 /* do not allow us to use a server path that doesn't
1266 exist on this system. this handles cases where
1267 the user has an RC file listing a serverpath
1268 from some other machine.
1270 vector<string>::iterator x;
1271 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1272 if (*x == strval) {
1273 break;
1276 if (x != server_strings.end()) {
1277 serverpath_combo.set_active_text (strval);
1278 } else {
1279 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1280 strval)
1281 << endmsg;
1285 } else if (child->name() == "driver") {
1286 driver_combo.set_active_text(strval);
1287 } else if (child->name() == "interface") {
1288 interface_combo.set_active_text(strval);
1289 if (!strval.empty()) {
1290 _interface_chosen = true;
1292 } else if (child->name() == "timeout") {
1293 timeout_combo.set_active_text(strval);
1294 } else if (child->name() == "dither") {
1295 dither_mode_combo.set_active_text(strval);
1296 } else if (child->name() == "audiomode") {
1297 audio_mode_combo.set_active_text(strval);
1298 } else if (child->name() == "inputdevice") {
1299 input_device_combo.set_active_text(strval);
1300 } else if (child->name() == "outputdevice") {
1301 output_device_combo.set_active_text(strval);