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));
221 #if PROVIDE_TOO_MANY_OPTIONS
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);
229 priority_spinner.set_value (60);
231 options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
233 options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
235 options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
237 options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
239 options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
241 options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
243 options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
245 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
248 options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
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);
267 #endif /* PROVIDE_TOO_MANY_OPTIONS */
268 label = manage (new Label (_("Number of ports")));
269 label->set_alignment (1.0, 0.5);
270 options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
271 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
275 label = manage (new Label (_("Dither")));
276 label->set_alignment (1.0, 0.5);
277 options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0));
278 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
282 find_jack_servers (server_strings);
284 if (server_strings.empty()) {
285 fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg;
289 set_popdown_strings (serverpath_combo, server_strings);
290 serverpath_combo.set_active_text (server_strings.front());
292 if (server_strings.size() > 1) {
293 label = manage (new Label (_("Server:")));
294 options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
295 label->set_alignment (0.0, 0.5);
296 options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0);
300 /* device settings */
302 device_packer.set_spacings (6);
306 label = manage (new Label (_("Input device")));
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 (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
311 label = manage (new Label (_("Output device")));
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 (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
317 label = manage (new Label (_("Input channels")));
318 label->set_alignment (1.0, 0.5);
319 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
320 device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
322 label = manage (new Label (_("Output channels")));
323 label->set_alignment (1.0, 0.5);
324 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
325 device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
327 label = manage (new Label (_("Hardware input latency (samples)")));
328 label->set_alignment (1.0, 0.5);
329 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
330 device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
332 label = manage (new Label (_("Hardware output latency (samples)")));
333 label->set_alignment (1.0, 0.5);
334 device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0);
335 device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0);
338 basic_hbox.pack_start (basic_packer, false, false);
339 options_hbox.pack_start (options_packer, false, false);
341 device_packer.set_border_width (12);
342 options_packer.set_border_width (12);
343 basic_packer.set_border_width (12);
345 notebook.pages().push_back (TabElem (basic_hbox, _("Device")));
346 notebook.pages().push_back (TabElem (options_hbox, _("Options")));
347 notebook.pages().push_back (TabElem (device_packer, _("Advanced")));
348 notebook.set_border_width (12);
350 set_border_width (12);
351 pack_start (notebook);
354 EngineControl::~EngineControl ()
360 EngineControl::build_command_line (vector<string>& cmd)
364 bool using_oss = false;
365 bool using_alsa = false;
366 bool using_coreaudio = false;
367 bool using_netjack = false;
368 bool using_ffado = false;
369 bool using_dummy = false;
371 /* first, path to jackd */
373 cmd.push_back (serverpath_combo.get_active_text ());
375 /* now jackd arguments */
377 str = timeout_combo.get_active_text ();
378 if (str != _("Ignore")) {
382 msecs = (uint32_t) floor (secs * 1000.0);
384 cmd.push_back ("-t");
385 cmd.push_back (to_string (msecs, std::dec));
389 if (no_memory_lock_button.get_active()) {
390 cmd.push_back ("-m"); /* no munlock */
393 cmd.push_back ("-p"); /* port max */
394 cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec));
396 if (realtime_button.get_active()) {
397 cmd.push_back ("-R");
398 cmd.push_back ("-P");
399 cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec));
402 if (unlock_memory_button.get_active()) {
403 cmd.push_back ("-u");
406 if (verbose_output_button.get_active()) {
407 cmd.push_back ("-v");
410 /* now add fixed arguments (not user-selectable) */
412 cmd.push_back ("-T"); // temporary */
414 /* next the driver */
416 cmd.push_back ("-d");
418 driver = driver_combo.get_active_text ();
419 if (driver == X_("ALSA")) {
421 cmd.push_back ("alsa");
422 } else if (driver == X_("OSS")) {
424 cmd.push_back ("oss");
425 } else if (driver == X_("CoreAudio")) {
426 using_coreaudio = true;
427 cmd.push_back ("coreaudio");
428 } else if (driver == X_("NetJACK")) {
429 using_netjack = true;
430 cmd.push_back ("netjack");
431 } else if (driver == X_("FFADO")) {
434 /* do this until FFADO becomes the standard */
436 char* hack = getenv ("ARDOUR_FIREWIRE_DRIVER_NAME");
439 cmd.push_back (hack);
441 cmd.push_back ("freebob");
444 } else if ( driver == X_("Dummy")) {
446 cmd.push_back ("dummy");
449 /* driver arguments */
451 if (!using_coreaudio) {
452 str = audio_mode_combo.get_active_text();
454 if (str == _("Playback/Recording on 1 Device")) {
458 } else if (str == _("Playback/Recording on 2 Devices")) {
460 string input_device = get_device_name (driver, input_device_combo.get_active_text());
461 string output_device = get_device_name (driver, output_device_combo.get_active_text());
463 if (input_device.empty() || output_device.empty()) {
468 cmd.push_back ("-C");
469 cmd.push_back (input_device);
470 cmd.push_back ("-P");
471 cmd.push_back (output_device);
473 } else if (str == _("Playback only")) {
474 cmd.push_back ("-P");
475 } else if (str == _("Recording only")) {
476 cmd.push_back ("-C");
479 if (! using_dummy ) {
480 cmd.push_back ("-n");
481 cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec));
485 cmd.push_back ("-r");
486 cmd.push_back (to_string (get_rate(), std::dec));
488 cmd.push_back ("-p");
489 cmd.push_back (period_size_combo.get_active_text());
493 if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) {
495 string device = get_device_name (driver, interface_combo.get_active_text());
496 if (device.empty()) {
501 cmd.push_back ("-d");
502 cmd.push_back (device);
505 if (hw_meter_button.get_active()) {
506 cmd.push_back ("-M");
509 if (hw_monitor_button.get_active()) {
510 cmd.push_back ("-H");
513 str = dither_mode_combo.get_active_text();
515 if (str == _("None")) {
516 } else if (str == _("Triangular")) {
517 cmd.push_back ("-z triangular");
518 } else if (str == _("Rectangular")) {
519 cmd.push_back ("-z rectangular");
520 } else if (str == _("Shaped")) {
521 cmd.push_back ("-z shaped");
524 if (force16bit_button.get_active()) {
525 cmd.push_back ("-S");
528 if (soft_mode_button.get_active()) {
529 cmd.push_back ("-s");
532 } else if (using_coreaudio) {
535 // note: older versions of the CoreAudio JACK backend use -n instead of -d here
537 string device = get_device_name (driver, interface_combo.get_active_text());
538 if (device.empty()) {
543 cmd.push_back ("-d");
544 cmd.push_back (device);
547 } else if (using_oss) {
549 } else if (using_netjack) {
555 EngineControl::engine_running ()
557 jack_status_t status;
558 jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status);
561 jack_client_close (c);
568 EngineControl::setup_engine ()
571 std::string cwd = "/tmp";
573 build_command_line (args);
576 return 1; // try again
579 Glib::ustring jackdrc_path = Glib::get_home_dir();
580 jackdrc_path += "/.jackdrc";
582 ofstream jackdrc (jackdrc_path.c_str());
584 error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
587 cerr << "JACK COMMAND: ";
588 for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
590 jackdrc << (*i) << ' ';
602 EngineControl::realtime_changed ()
605 priority_spinner.set_sensitive (realtime_button.get_active());
610 EngineControl::enumerate_devices (const string& driver)
612 /* note: case matters for the map keys */
614 if (driver == "CoreAudio") {
616 devices[driver] = enumerate_coreaudio_devices ();
620 } else if (driver == "ALSA") {
621 devices[driver] = enumerate_alsa_devices ();
622 } else if (driver == "FFADO") {
623 devices[driver] = enumerate_ffado_devices ();
624 } else if (driver == "OSS") {
625 devices[driver] = enumerate_oss_devices ();
626 } else if (driver == "Dummy") {
627 devices[driver] = enumerate_dummy_devices ();
628 } else if (driver == "NetJACK") {
629 devices[driver] = enumerate_netjack_devices ();
638 getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize)
640 UInt32 size = sizeof(CFStringRef);
642 OSStatus res = AudioDeviceGetProperty(id, 0, false,
643 kAudioDevicePropertyDeviceUID, &size, &UI);
645 CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding());
651 EngineControl::enumerate_coreaudio_devices ()
655 // Find out how many Core Audio devices are there, if any...
656 // (code snippet gently "borrowed" from St?hane Letz jackdmp;)
659 size_t outSize = sizeof(isWritable);
661 backend_devs.clear ();
663 err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
664 &outSize, &isWritable);
666 // Calculate the number of device available...
667 int numCoreDevices = outSize / sizeof(AudioDeviceID);
668 // Make space for the devices we are about to get...
669 AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices];
670 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
671 &outSize, (void *) coreDeviceIDs);
673 // Look for the CoreAudio device name...
674 char coreDeviceName[256];
677 for (int i = 0; i < numCoreDevices; i++) {
679 nameSize = sizeof (coreDeviceName);
681 /* enforce duplex devices only */
683 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
684 0, true, kAudioDevicePropertyStreams,
685 &outSize, &isWritable);
687 if (err != noErr || outSize == 0) {
691 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
692 0, false, kAudioDevicePropertyStreams,
693 &outSize, &isWritable);
695 if (err != noErr || outSize == 0) {
699 err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i],
700 0, true, kAudioDevicePropertyDeviceName,
701 &outSize, &isWritable);
703 err = AudioDeviceGetProperty(coreDeviceIDs[i],
704 0, true, kAudioDevicePropertyDeviceName,
705 &nameSize, (void *) coreDeviceName);
707 char drivername[128];
709 // this returns the unique id for the device
710 // that must be used on the commandline for jack
712 if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) {
713 devs.push_back (coreDeviceName);
714 backend_devs.push_back (drivername);
720 delete [] coreDeviceIDs;
724 if (devs.size() == 0) {
725 MessageDialog msg (_("\
726 You do not have any audio devices capable of\n\
727 simultaneous playback and recording.\n\n\
728 Please use Applications -> Utilities -> Audio MIDI Setup\n\
729 to create an \"aggregrate\" device, or install a suitable\n\
730 audio interface.\n\n\
731 Please send email to Apple and ask them why new Macs\n\
732 have no duplex audio device.\n\n\
733 Alternatively, if you really want just playback\n\
734 or recording but not both, start JACK before running\n\
735 Ardour and choose the relevant device then."
737 true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);
738 msg.set_title (_("No suitable audio devices"));
739 msg.set_position (Gtk::WIN_POS_MOUSE);
749 EngineControl::enumerate_alsa_devices ()
754 snd_ctl_card_info_t *info;
755 snd_pcm_info_t *pcminfo;
756 snd_ctl_card_info_alloca(&info);
757 snd_pcm_info_alloca(&pcminfo);
762 backend_devs.clear ();
764 while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
767 devname += to_string (cardnum, std::dec);
769 if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
771 while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
773 bool have_playback = false;
774 bool have_capture = false;
776 /* find duplex devices only */
778 snd_pcm_info_set_device (pcminfo, device);
779 snd_pcm_info_set_subdevice (pcminfo, 0);
780 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE);
782 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
786 snd_pcm_info_set_device (pcminfo, device);
787 snd_pcm_info_set_subdevice (pcminfo, 0);
788 snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK);
790 if (snd_ctl_pcm_info (handle, pcminfo) >= 0) {
791 have_playback = true;
794 if (have_capture && have_playback) {
795 devs.push_back (snd_pcm_info_get_name (pcminfo));
797 devname += to_string (device, std::dec);
798 backend_devs.push_back (devname);
802 snd_ctl_close(handle);
810 EngineControl::enumerate_ffado_devices ()
813 backend_devs.clear ();
818 EngineControl::enumerate_freebob_devices ()
824 EngineControl::enumerate_oss_devices ()
830 EngineControl::enumerate_dummy_devices ()
836 EngineControl::enumerate_netjack_devices ()
844 EngineControl::driver_changed ()
846 string driver = driver_combo.get_active_text();
847 string::size_type maxlen = 0;
851 enumerate_devices (driver);
853 vector<string>& strings = devices[driver];
855 if (strings.empty() && driver != "FFADO" && driver != "Dummy") {
856 error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
860 for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) {
861 if ((*i).length() > maxlen) {
862 maxlen = (*i).length();
867 set_popdown_strings (interface_combo, strings);
868 set_popdown_strings (input_device_combo, strings);
869 set_popdown_strings (output_device_combo, strings);
871 if (!strings.empty()) {
872 interface_combo.set_active_text (strings.front());
873 input_device_combo.set_active_text (strings.front());
874 output_device_combo.set_active_text (strings.front());
877 if (driver == "ALSA") {
878 soft_mode_button.set_sensitive (true);
879 force16bit_button.set_sensitive (true);
880 hw_monitor_button.set_sensitive (true);
881 hw_meter_button.set_sensitive (true);
882 monitor_button.set_sensitive (true);
884 soft_mode_button.set_sensitive (false);
885 force16bit_button.set_sensitive (false);
886 hw_monitor_button.set_sensitive (false);
887 hw_meter_button.set_sensitive (false);
888 monitor_button.set_sensitive (false);
893 EngineControl::get_rate ()
895 return atoi (sample_rate_combo.get_active_text ());
899 EngineControl::redisplay_latency ()
901 uint32_t rate = get_rate();
905 float periods = periods_adjustment.get_value();
907 float period_size = atof (period_size_combo.get_active_text());
910 snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0));
912 latency_label.set_text (buf);
916 EngineControl::audio_mode_changed ()
918 Glib::ustring str = audio_mode_combo.get_active_text();
920 if (str == _("Playback/Recording on 1 Device")) {
921 input_device_combo.set_sensitive (false);
922 output_device_combo.set_sensitive (false);
923 } else if (str == _("Playback/Recording on 2 Devices")) {
924 input_device_combo.set_sensitive (true);
925 output_device_combo.set_sensitive (true);
926 } else if (str == _("Playback only")) {
927 output_device_combo.set_sensitive (true);
928 } else if (str == _("Recording only")) {
929 input_device_combo.set_sensitive (true);
933 static bool jack_server_filter(const string& str, void *arg)
935 return str == "jackd" || str == "jackdmp";
939 EngineControl::find_jack_servers (vector<string>& strings)
942 /* this magic lets us finds the path to the OSX bundle, and then
943 we infer JACK's location from there
946 char execpath[MAXPATHLEN+1];
947 uint32_t pathsz = sizeof (execpath);
949 _NSGetExecutablePath (execpath, &pathsz);
951 string path (Glib::path_get_dirname (execpath));
954 if (Glib::file_test (path, FILE_TEST_EXISTS)) {
955 strings.push_back (path);
958 if (getenv ("ARDOUR_WITH_JACK")) {
959 /* no other options - only use the JACK we supply */
960 if (strings.empty()) {
961 fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg;
971 vector<string *> *jack_servers;
972 std::map<string,int> un;
974 bool need_minimal_path = false;
981 need_minimal_path = true;
985 // many mac users don't have PATH set up to include
986 // likely installed locations of JACK
987 need_minimal_path = true;
990 if (need_minimal_path) {
992 path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
994 path += ":/usr/local/bin:/opt/local/bin";
999 // push it back into the environment so that auto-started JACK can find it.
1000 // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
1001 setenv ("PATH", path.c_str(), 1);
1004 jack_servers = scanner (path, jack_server_filter, 0, false, true);
1006 vector<string *>::iterator iter;
1008 for (iter = jack_servers->begin(); iter != jack_servers->end(); iter++) {
1012 strings.push_back(p);
1019 EngineControl::get_device_name (const string& driver, const string& human_readable)
1021 vector<string>::iterator n;
1022 vector<string>::iterator i;
1024 if (human_readable.empty()) {
1025 /* this can happen if the user's .ardourrc file has a device name from
1026 another computer system in it
1028 MessageDialog msg (_("You need to choose an audio device first."));
1033 if (backend_devs.empty()) {
1034 return human_readable;
1037 for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) {
1038 if (human_readable == (*i)) {
1043 if (i == devices[driver].end()) {
1044 warning << string_compose (_("Audio device \"%1\" not known on this computer."), human_readable) << endmsg;
1051 EngineControl::get_state ()
1053 XMLNode* root = new XMLNode ("AudioSetup");
1057 child = new XMLNode ("periods");
1058 child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec));
1059 root->add_child_nocopy (*child);
1061 child = new XMLNode ("priority");
1062 child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec));
1063 root->add_child_nocopy (*child);
1065 child = new XMLNode ("ports");
1066 child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec));
1067 root->add_child_nocopy (*child);
1069 child = new XMLNode ("inchannels");
1070 child->add_property ("val", to_string (input_channels.get_value(), std::dec));
1071 root->add_child_nocopy (*child);
1073 child = new XMLNode ("outchannels");
1074 child->add_property ("val", to_string (output_channels.get_value(), std::dec));
1075 root->add_child_nocopy (*child);
1077 child = new XMLNode ("inlatency");
1078 child->add_property ("val", to_string (input_latency.get_value(), std::dec));
1079 root->add_child_nocopy (*child);
1081 child = new XMLNode ("outlatency");
1082 child->add_property ("val", to_string (output_latency.get_value(), std::dec));
1083 root->add_child_nocopy (*child);
1085 child = new XMLNode ("realtime");
1086 child->add_property ("val", to_string (realtime_button.get_active(), std::dec));
1087 root->add_child_nocopy (*child);
1089 child = new XMLNode ("nomemorylock");
1090 child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec));
1091 root->add_child_nocopy (*child);
1093 child = new XMLNode ("unlockmemory");
1094 child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec));
1095 root->add_child_nocopy (*child);
1097 child = new XMLNode ("softmode");
1098 child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec));
1099 root->add_child_nocopy (*child);
1101 child = new XMLNode ("force16bit");
1102 child->add_property ("val", to_string (force16bit_button.get_active(), std::dec));
1103 root->add_child_nocopy (*child);
1105 child = new XMLNode ("hwmonitor");
1106 child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec));
1107 root->add_child_nocopy (*child);
1109 child = new XMLNode ("hwmeter");
1110 child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec));
1111 root->add_child_nocopy (*child);
1113 child = new XMLNode ("verbose");
1114 child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec));
1115 root->add_child_nocopy (*child);
1117 child = new XMLNode ("samplerate");
1118 child->add_property ("val", sample_rate_combo.get_active_text());
1119 root->add_child_nocopy (*child);
1121 child = new XMLNode ("periodsize");
1122 child->add_property ("val", period_size_combo.get_active_text());
1123 root->add_child_nocopy (*child);
1125 child = new XMLNode ("serverpath");
1126 child->add_property ("val", serverpath_combo.get_active_text());
1127 root->add_child_nocopy (*child);
1129 child = new XMLNode ("driver");
1130 child->add_property ("val", driver_combo.get_active_text());
1131 root->add_child_nocopy (*child);
1133 child = new XMLNode ("interface");
1134 child->add_property ("val", interface_combo.get_active_text());
1135 root->add_child_nocopy (*child);
1137 child = new XMLNode ("timeout");
1138 child->add_property ("val", timeout_combo.get_active_text());
1139 root->add_child_nocopy (*child);
1141 child = new XMLNode ("dither");
1142 child->add_property ("val", dither_mode_combo.get_active_text());
1143 root->add_child_nocopy (*child);
1145 child = new XMLNode ("audiomode");
1146 child->add_property ("val", audio_mode_combo.get_active_text());
1147 root->add_child_nocopy (*child);
1149 child = new XMLNode ("inputdevice");
1150 child->add_property ("val", input_device_combo.get_active_text());
1151 root->add_child_nocopy (*child);
1153 child = new XMLNode ("outputdevice");
1154 child->add_property ("val", output_device_combo.get_active_text());
1155 root->add_child_nocopy (*child);
1161 EngineControl::set_state (const XMLNode& root)
1164 XMLNodeConstIterator citer;
1166 XMLProperty* prop = NULL;
1167 bool using_dummy = false;
1172 if ( (child = root.child ("driver"))){
1173 prop = child->property("val");
1174 if (prop && (prop->value() == "Dummy") ) {
1179 clist = root.children();
1181 for (citer = clist.begin(); citer != clist.end(); ++citer) {
1182 if ( prop && (prop->value() == "FFADO" ))
1186 prop = child->property ("val");
1188 if (!prop || prop->value().empty()) {
1190 if ( using_dummy && ( child->name() == "interface" || child->name() == "inputdevice" || child->name() == "outputdevice" ))
1192 error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg;
1196 strval = prop->value();
1198 /* adjustments/spinners */
1200 if (child->name() == "periods") {
1201 val = atoi (strval);
1202 periods_adjustment.set_value(val);
1203 } else if (child->name() == "priority") {
1204 val = atoi (strval);
1205 priority_adjustment.set_value(val);
1206 } else if (child->name() == "ports") {
1207 val = atoi (strval);
1208 ports_adjustment.set_value(val);
1209 } else if (child->name() == "inchannels") {
1210 val = atoi (strval);
1211 input_channels.set_value(val);
1212 } else if (child->name() == "outchannels") {
1213 val = atoi (strval);
1214 output_channels.set_value(val);
1215 } else if (child->name() == "inlatency") {
1216 val = atoi (strval);
1217 input_latency.set_value(val);
1218 } else if (child->name() == "outlatency") {
1219 val = atoi (strval);
1220 output_latency.set_value(val);
1225 else if (child->name() == "realtime") {
1226 val = atoi (strval);
1227 realtime_button.set_active(val);
1228 } else if (child->name() == "nomemorylock") {
1229 val = atoi (strval);
1230 no_memory_lock_button.set_active(val);
1231 } else if (child->name() == "unlockmemory") {
1232 val = atoi (strval);
1233 unlock_memory_button.set_active(val);
1234 } else if (child->name() == "softmode") {
1235 val = atoi (strval);
1236 soft_mode_button.set_active(val);
1237 } else if (child->name() == "force16bit") {
1238 val = atoi (strval);
1239 force16bit_button.set_active(val);
1240 } else if (child->name() == "hwmonitor") {
1241 val = atoi (strval);
1242 hw_monitor_button.set_active(val);
1243 } else if (child->name() == "hwmeter") {
1244 val = atoi (strval);
1245 hw_meter_button.set_active(val);
1246 } else if (child->name() == "verbose") {
1247 val = atoi (strval);
1248 verbose_output_button.set_active(val);
1253 else if (child->name() == "samplerate") {
1254 sample_rate_combo.set_active_text(strval);
1255 } else if (child->name() == "periodsize") {
1256 period_size_combo.set_active_text(strval);
1257 } else if (child->name() == "serverpath") {
1258 /* do not allow us to use a server path that doesn't
1259 exist on this system. this handles cases where
1260 the user has an RC file listing a serverpath
1261 from some other machine.
1263 vector<string>::iterator x;
1264 for (x = server_strings.begin(); x != server_strings.end(); ++x) {
1269 if (x != server_strings.end()) {
1270 serverpath_combo.set_active_text (strval);
1272 warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"),
1276 } else if (child->name() == "driver") {
1277 driver_combo.set_active_text(strval);
1278 } else if (child->name() == "interface") {
1279 interface_combo.set_active_text(strval);
1280 } else if (child->name() == "timeout") {
1281 timeout_combo.set_active_text(strval);
1282 } else if (child->name() == "dither") {
1283 dither_mode_combo.set_active_text(strval);
1284 } else if (child->name() == "audiomode") {
1285 audio_mode_combo.set_active_text(strval);
1286 } else if (child->name() == "inputdevice") {
1287 input_device_combo.set_active_text(strval);
1288 } else if (child->name() == "outputdevice") {
1289 output_device_combo.set_active_text(strval);