7 #include <gtkmm/messagedialog.h>
11 #include <CoreAudio/CoreAudio.h>
12 #include <CoreFoundation/CFString.h>
13 #include <sys/param.h>
14 #include <mach-o/dyld.h>
16 #include <alsa/asoundlib.h>
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>
33 #include "engine_dialog.h"
38 using namespace Gtkmm2ext;
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")),
62 options_packer (4, 2),
66 options_packer (14, 2),
70 using namespace Notebook_Helpers;
72 vector<string> strings;
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");
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");
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);
114 strings.push_back (X_("CoreAudio"));
116 strings.push_back (X_("ALSA"));
117 strings.push_back (X_("OSS"));
118 strings.push_back (X_("FFADO"));
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));
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 ();
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);
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);
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);
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);
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);
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);
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));
180 /* no audio mode with CoreAudio, its duplex or nuthin' */
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);
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);
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);
212 options_packer.set_spacings (6);
215 options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
218 realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed));
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);
227 priority_spinner.set_value (60);
229 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
231 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
233 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
235 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
237 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
239 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
241 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
243 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
246 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
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);
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);
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);
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;
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);
297 /* device settings */
299 device_packer.set_spacings (6);
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);
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);
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);
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);
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);
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);
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 ()
357 EngineControl::build_command_line (vector<string>& cmd)
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")) {
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")) {
416 cmd.push_back ("alsa");
417 } else if (driver == X_("OSS")) {
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")) {
429 /* do this until FFADO becomes the standard */
431 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
434 cmd.push_back (hack);
436 cmd.push_back ("freebob");
439 } else if ( driver == X_("Dummy")) {
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")) {
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()) {
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());
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()) {
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) {
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()) {
538 cmd.push_back ("-d");
539 cmd.push_back (device);
542 } else if (using_oss) {
544 } else if (using_netjack) {
550 EngineControl::engine_running ()
552 jack_status_t status;
553 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
556 jack_client_close (c);
563 EngineControl::setup_engine ()
566 std::string cwd = "/tmp";
568 build_command_line (args);
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());
579 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
582 cerr << "JACK COMMAND: ";
583 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
585 jackdrc << (*i) << ' ';
597 EngineControl::realtime_changed ()
600 priority_spinner.set_sensitive (realtime_button.get_active());
605 EngineControl::enumerate_devices (const string& driver)
607 /* note: case matters for the map keys */
609 if (driver == "CoreAudio") {
611 devices[driver] = enumerate_coreaudio_devices ();
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 ();
633 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
635 UInt32 size = sizeof(CFStringRef);
637 OSStatus res = AudioDeviceGetProperty(id, 0, false,
638 kAudioDevicePropertyDeviceUID, &size, &UI);
640 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
646 EngineControl::enumerate_coreaudio_devices ()
650 // Find out how many Core Audio devices are there, if any...
651 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
654 size_t outSize = sizeof(isWritable);
656 backend_devs.clear ();
658 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
659 &outSize, &isWritable);
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);
668 // Look for the CoreAudio device name...
669 char coreDeviceName[256];
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) {
686 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
687 0, false, kAudioDevicePropertyStreams,
688 &outSize, &isWritable);
690 if (err != noErr || outSize == 0) {
694 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
695 0, true, kAudioDevicePropertyDeviceName,
696 &outSize, &isWritable);
698 err = AudioDeviceGetProperty(coreDeviceIDs[i],
699 0, true, kAudioDevicePropertyDeviceName,
700 &nameSize, (void *) coreDeviceName);
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);
744 EngineControl::enumerate_alsa_devices ()
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);
757 backend_devs.clear ();
759 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
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) {
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));
792 devname += to_string (device, std::dec);
793 backend_devs.push_back (devname);
797 snd_ctl_close(handle);
805 EngineControl::enumerate_ffado_devices ()
808 backend_devs.clear ();
813 EngineControl::enumerate_freebob_devices ()
819 EngineControl::enumerate_oss_devices ()
825 EngineControl::enumerate_dummy_devices ()
831 EngineControl::enumerate_netjack_devices ()
839 EngineControl::driver_changed ()
841 string driver = driver_combo.get_active_text();
842 string::size_type maxlen = 0;
846 enumerate_devices (driver);
848 vector<string>& strings = devices[driver];
850 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
851 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
855 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
856 if ((*i).length() > maxlen) {
857 maxlen = (*i).length();
862 set_popdown_strings (interface_combo, strings);
863 set_popdown_strings (input_device_combo, strings);
864 set_popdown_strings (output_device_combo, strings);
866 if (!strings.empty()) {
867 interface_combo.set_active_text (strings.front());
868 input_device_combo.set_active_text (strings.front());
869 output_device_combo.set_active_text (strings.front());
872 if (driver == "ALSA") {
873 soft_mode_button.set_sensitive (true);
874 force16bit_button.set_sensitive (true);
875 hw_monitor_button.set_sensitive (true);
876 hw_meter_button.set_sensitive (true);
877 monitor_button.set_sensitive (true);
879 soft_mode_button.set_sensitive (false);
880 force16bit_button.set_sensitive (false);
881 hw_monitor_button.set_sensitive (false);
882 hw_meter_button.set_sensitive (false);
883 monitor_button.set_sensitive (false);
888 EngineControl::get_rate ()
890 return atoi (sample_rate_combo.get_active_text ());
894 EngineControl::redisplay_latency ()
896 uint32_t rate = get_rate();
900 float periods = periods_adjustment.get_value();
902 float period_size = atof (period_size_combo.get_active_text());
905 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
907 latency_label.set_text (buf);
911 EngineControl::audio_mode_changed ()
913 Glib::ustring str = audio_mode_combo.get_active_text();
915 if (str == _("Playback/Recording on 1 Device")) {
916 input_device_combo.set_sensitive (false);
917 output_device_combo.set_sensitive (false);
918 } else if (str == _("Playback/Recording on 2 Devices")) {
919 input_device_combo.set_sensitive (true);
920 output_device_combo.set_sensitive (true);
921 } else if (str == _("Playback only")) {
922 output_device_combo.set_sensitive (true);
923 } else if (str == _("Recording only")) {
924 input_device_combo.set_sensitive (true);
928 static bool jack_server_filter(const string& str, void *arg)
930 return str == "jackd" || str == "jackdmp";
934 EngineControl::find_jack_servers (vector<string>& strings)
937 /* this magic lets us finds the path to the OSX bundle, and then
938 we infer JACK's location from there
941 char execpath[MAXPATHLEN+1];
942 uint32_t pathsz = sizeof (execpath);
944 _NSGetExecutablePath (execpath, &pathsz);
946 string path (Glib::path_get_dirname (execpath));
949 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
950 strings.push_back (path);
953 if (getenv ("ARDOUR_WITH_JACK")) {
954 /* no other options - only use the JACK we supply */
955 if (strings.empty()) {
956 fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
966 vector<string *> *jack_servers;
967 std::map<string,int> un;
969 bool need_minimal_path = false;
976 need_minimal_path = true;
980 // many mac users don't have PATH set up to include
981 // likely installed locations of JACK
982 need_minimal_path = true;
985 if (need_minimal_path) {
987 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
989 path += ":/usr/local/bin:/opt/local/bin";
994 // push it back into the environment so that auto-started JACK can find it.
995 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
996 setenv ("PATH", path.c_str(), 1);
999 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1001 vector<string *>::iterator iter;
1003 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1007 strings.push_back(p);
1014 EngineControl::get_device_name (const string& driver, const string& human_readable)
1016 vector<string>::iterator n;
1017 vector<string>::iterator i;
1019 if (human_readable.empty()) {
1020 /* this can happen if the user's .ardourrc file has a device name from
1021 another computer system in it
1023 MessageDialog msg (_("You need to choose an audio device first."));
1028 if (backend_devs.empty()) {
1029 return human_readable;
1032 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1033 if (human_readable == (*i)) {
1038 if (i == devices[driver].end()) {
1039 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1046 EngineControl::get_state ()
1048 XMLNode* root = new XMLNode ("AudioSetup");
1052 child = new XMLNode ("periods");
1053 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1054 root->add_child_nocopy (*child);
1056 child = new XMLNode ("priority");
1057 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1058 root->add_child_nocopy (*child);
1060 child = new XMLNode ("ports");
1061 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1062 root->add_child_nocopy (*child);
1064 child = new XMLNode ("inchannels");
1065 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1066 root->add_child_nocopy (*child);
1068 child = new XMLNode ("outchannels");
1069 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1070 root->add_child_nocopy (*child);
1072 child = new XMLNode ("inlatency");
1073 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1074 root->add_child_nocopy (*child);
1076 child = new XMLNode ("outlatency");
1077 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1078 root->add_child_nocopy (*child);
1080 child = new XMLNode ("realtime");
1081 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1082 root->add_child_nocopy (*child);
1084 child = new XMLNode ("nomemorylock");
1085 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1086 root->add_child_nocopy (*child);
1088 child = new XMLNode ("unlockmemory");
1089 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1090 root->add_child_nocopy (*child);
1092 child = new XMLNode ("softmode");
1093 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1094 root->add_child_nocopy (*child);
1096 child = new XMLNode ("force16bit");
1097 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1098 root->add_child_nocopy (*child);
1100 child = new XMLNode ("hwmonitor");
1101 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1102 root->add_child_nocopy (*child);
1104 child = new XMLNode ("hwmeter");
1105 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1106 root->add_child_nocopy (*child);
1108 child = new XMLNode ("verbose");
1109 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1110 root->add_child_nocopy (*child);
1112 child = new XMLNode ("samplerate");
1113 child->add_property ("val", sample_rate_combo.get_active_text());
1114 root->add_child_nocopy (*child);
1116 child = new XMLNode ("periodsize");
1117 child->add_property ("val", period_size_combo.get_active_text());
1118 root->add_child_nocopy (*child);
1120 child = new XMLNode ("serverpath");
1121 child->add_property ("val", serverpath_combo.get_active_text());
1122 root->add_child_nocopy (*child);
1124 child = new XMLNode ("driver");
1125 child->add_property ("val", driver_combo.get_active_text());
1126 root->add_child_nocopy (*child);
1128 child = new XMLNode ("interface");
1129 child->add_property ("val", interface_combo.get_active_text());
1130 root->add_child_nocopy (*child);
1132 child = new XMLNode ("timeout");
1133 child->add_property ("val", timeout_combo.get_active_text());
1134 root->add_child_nocopy (*child);
1136 child = new XMLNode ("dither");
1137 child->add_property ("val", dither_mode_combo.get_active_text());
1138 root->add_child_nocopy (*child);
1140 child = new XMLNode ("audiomode");
1141 child->add_property ("val", audio_mode_combo.get_active_text());
1142 root->add_child_nocopy (*child);
1144 child = new XMLNode ("inputdevice");
1145 child->add_property ("val", input_device_combo.get_active_text());
1146 root->add_child_nocopy (*child);
1148 child = new XMLNode ("outputdevice");
1149 child->add_property ("val", output_device_combo.get_active_text());
1150 root->add_child_nocopy (*child);
1156 EngineControl::set_state (const XMLNode& root)
1159 XMLNodeConstIterator citer;
1161 XMLProperty* prop = NULL;
1162 bool using_dummy = false;
1167 if ( (child = root.child ("driver"))){
1168 prop = child->property("val");
1169 if (prop && (prop->value() == "Dummy") ) {
1174 clist = root.children();
1176 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1177 if ( prop && (prop->value() == "FFADO" ))
1181 prop = child->property ("val");
1183 if (!prop || prop->value().empty()) {
1185 if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1187 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1191 strval = prop->value();
1193 /* adjustments/spinners */
1195 if (child->name() == "periods") {
1196 val = atoi (strval);
1197 periods_adjustment.set_value(val);
1198 } else if (child->name() == "priority") {
1199 val = atoi (strval);
1200 priority_adjustment.set_value(val);
1201 } else if (child->name() == "ports") {
1202 val = atoi (strval);
1203 ports_adjustment.set_value(val);
1204 } else if (child->name() == "inchannels") {
1205 val = atoi (strval);
1206 input_channels.set_value(val);
1207 } else if (child->name() == "outchannels") {
1208 val = atoi (strval);
1209 output_channels.set_value(val);
1210 } else if (child->name() == "inlatency") {
1211 val = atoi (strval);
1212 input_latency.set_value(val);
1213 } else if (child->name() == "outlatency") {
1214 val = atoi (strval);
1215 output_latency.set_value(val);
1220 else if (child->name() == "realtime") {
1221 val = atoi (strval);
1222 realtime_button.set_active(val);
1223 } else if (child->name() == "nomemorylock") {
1224 val = atoi (strval);
1225 no_memory_lock_button.set_active(val);
1226 } else if (child->name() == "unlockmemory") {
1227 val = atoi (strval);
1228 unlock_memory_button.set_active(val);
1229 } else if (child->name() == "softmode") {
1230 val = atoi (strval);
1231 soft_mode_button.set_active(val);
1232 } else if (child->name() == "force16bit") {
1233 val = atoi (strval);
1234 force16bit_button.set_active(val);
1235 } else if (child->name() == "hwmonitor") {
1236 val = atoi (strval);
1237 hw_monitor_button.set_active(val);
1238 } else if (child->name() == "hwmeter") {
1239 val = atoi (strval);
1240 hw_meter_button.set_active(val);
1241 } else if (child->name() == "verbose") {
1242 val = atoi (strval);
1243 verbose_output_button.set_active(val);
1248 else if (child->name() == "samplerate") {
1249 sample_rate_combo.set_active_text(strval);
1250 } else if (child->name() == "periodsize") {
1251 period_size_combo.set_active_text(strval);
1252 } else if (child->name() == "serverpath") {
1253 /* do not allow us to use a server path that doesn't
1254 exist on this system. this handles cases where
1255 the user has an RC file listing a serverpath
1256 from some other machine.
1258 vector<string>::iterator x;
1259 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1264 if (x != server_strings.end()) {
1265 serverpath_combo.set_active_text (strval);
1267 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1271 } else if (child->name() == "driver") {
1272 driver_combo.set_active_text(strval);
1273 } else if (child->name() == "interface") {
1274 interface_combo.set_active_text(strval);
1275 } else if (child->name() == "timeout") {
1276 timeout_combo.set_active_text(strval);
1277 } else if (child->name() == "dither") {
1278 dither_mode_combo.set_active_text(strval);
1279 } else if (child->name() == "audiomode") {
1280 audio_mode_combo.set_active_text(strval);
1281 } else if (child->name() == "inputdevice") {
1282 input_device_combo.set_active_text(strval);
1283 } else if (child->name() == "outputdevice") {
1284 output_device_combo.set_active_text(strval);